diff --git a/.buildkite/ftr_security_serverless_configs.yml b/.buildkite/ftr_security_serverless_configs.yml index 22d1391034822..6b1c222382687 100644 --- a/.buildkite/ftr_security_serverless_configs.yml +++ b/.buildkite/ftr_security_serverless_configs.yml @@ -45,13 +45,23 @@ enabled: - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/actions/trial_license_complete_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/trial_license_complete_tier/configs/serverless.config.ts - - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/operators_data_types/date_numeric_types/basic_license_essentials_tier/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/operators_data_types/date_types/basic_license_essentials_tier/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/operators_data_types/float/basic_license_essentials_tier/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/operators_data_types/integer/basic_license_essentials_tier/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/operators_data_types/double/basic_license_essentials_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/operators_data_types/ips/basic_license_essentials_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/operators_data_types/keyword/basic_license_essentials_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/operators_data_types/long/basic_license_essentials_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/operators_data_types/text/basic_license_essentials_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/basic_license_essentials_tier/configs/serverless.config.ts - - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/eql/trial_license_complete_tier/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/esql/trial_license_complete_tier/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/general_logic/trial_license_complete_tier/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/indicator_match/trial_license_complete_tier/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/machine_learning/trial_license_complete_tier/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/new_terms/trial_license_complete_tier/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/query/trial_license_complete_tier/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/threshold/trial_license_complete_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_gaps/trial_license_complete_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/basic_license_essentials_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/trial_license_complete_tier/configs/serverless.config.ts @@ -96,6 +106,7 @@ enabled: - x-pack/test/security_solution_api_integration/test_suites/edr_workflows/metadata/trial_license_complete_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/edr_workflows/package/trial_license_complete_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/edr_workflows/policy_response/trial_license_complete_tier/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/edr_workflows/policy/trial_license_complete_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/edr_workflows/resolver/trial_license_complete_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/edr_workflows/response_actions/trial_license_complete_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/edr_workflows/spaces/trial_license_complete_tier/configs/serverless.config.ts diff --git a/.buildkite/ftr_security_stateful_configs.yml b/.buildkite/ftr_security_stateful_configs.yml index aa37c6f52fb8c..d3aadb1b7491d 100644 --- a/.buildkite/ftr_security_stateful_configs.yml +++ b/.buildkite/ftr_security_stateful_configs.yml @@ -30,13 +30,23 @@ enabled: - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/actions/trial_license_complete_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/trial_license_complete_tier/configs/ess.config.ts - - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/operators_data_types/date_numeric_types/basic_license_essentials_tier/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/operators_data_types/date_types/basic_license_essentials_tier/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/operators_data_types/float/basic_license_essentials_tier/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/operators_data_types/integer/basic_license_essentials_tier/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/operators_data_types/double/basic_license_essentials_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/operators_data_types/ips/basic_license_essentials_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/operators_data_types/keyword/basic_license_essentials_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/operators_data_types/long/basic_license_essentials_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/operators_data_types/text/basic_license_essentials_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/basic_license_essentials_tier/configs/ess.config.ts - - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/eql/trial_license_complete_tier/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/esql/trial_license_complete_tier/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/general_logic/trial_license_complete_tier/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/indicator_match/trial_license_complete_tier/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/machine_learning/trial_license_complete_tier/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/new_terms/trial_license_complete_tier/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/query/trial_license_complete_tier/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/threshold/trial_license_complete_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_gaps/trial_license_complete_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/basic_license_essentials_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/trial_license_complete_tier/configs/ess.config.ts @@ -83,6 +93,7 @@ enabled: - x-pack/test/security_solution_api_integration/test_suites/edr_workflows/metadata/trial_license_complete_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/edr_workflows/package/trial_license_complete_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/edr_workflows/policy_response/trial_license_complete_tier/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/edr_workflows/policy/trial_license_complete_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/edr_workflows/resolver/trial_license_complete_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/edr_workflows/response_actions/trial_license_complete_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/edr_workflows/spaces/trial_license_complete_tier/configs/ess.config.ts diff --git a/.buildkite/pipeline-utils/agent_images.ts b/.buildkite/pipeline-utils/agent_images.ts index a87ea8dca6234..821a0a6b92ecf 100644 --- a/.buildkite/pipeline-utils/agent_images.ts +++ b/.buildkite/pipeline-utils/agent_images.ts @@ -58,7 +58,7 @@ const expandAgentQueue = (queueName: string = 'n2-4-spot') => { const additionalProps = { spot: { preemptible: true }, - virt: { localSsdInterface: 'nvme', enableNestedVirtualization: true, localSsds: 1 }, + virt: { enableNestedVirtualization: true }, }[addition] || {}; return { diff --git a/.buildkite/pipelines/artifacts.yml b/.buildkite/pipelines/artifacts.yml index b6f873ad2bd14..4765077287615 100644 --- a/.buildkite/pipelines/artifacts.yml +++ b/.buildkite/pipelines/artifacts.yml @@ -21,8 +21,6 @@ steps: imageProject: elastic-images-prod provider: gcp enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 timeout_in_minutes: 30 retry: @@ -37,8 +35,6 @@ steps: imageProject: elastic-images-prod provider: gcp enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 timeout_in_minutes: 30 retry: @@ -53,8 +49,6 @@ steps: imageProject: elastic-images-prod provider: gcp enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 timeout_in_minutes: 30 retry: @@ -68,8 +62,6 @@ steps: image: family/kibana-ubuntu-2004 imageProject: elastic-images-prod provider: gcp - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-2 timeout_in_minutes: 30 retry: @@ -83,8 +75,6 @@ steps: image: family/kibana-ubuntu-2004 imageProject: elastic-images-prod provider: gcp - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-2 timeout_in_minutes: 30 retry: @@ -111,8 +101,6 @@ steps: image: family/kibana-ubuntu-2004 imageProject: elastic-images-prod provider: gcp - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-2 timeout_in_minutes: 30 retry: @@ -129,8 +117,6 @@ steps: image: family/kibana-ubuntu-2004 imageProject: elastic-images-prod provider: gcp - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-2 timeout_in_minutes: 60 if: "build.env('RELEASE_BUILD') == null || build.env('RELEASE_BUILD') == '' || build.env('RELEASE_BUILD') == 'false'" @@ -156,7 +142,5 @@ steps: image: family/kibana-ubuntu-2004 imageProject: elastic-images-prod provider: gcp - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-2 timeout_in_minutes: 30 diff --git a/.buildkite/pipelines/chrome_forward_testing.yml b/.buildkite/pipelines/chrome_forward_testing.yml index daf928cf2c162..eb80625a73d77 100644 --- a/.buildkite/pipelines/chrome_forward_testing.yml +++ b/.buildkite/pipelines/chrome_forward_testing.yml @@ -317,8 +317,6 @@ steps: label: 'Defend Workflows Cypress Tests' agents: enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 depends_on: - build @@ -333,8 +331,6 @@ steps: label: 'Defend Workflows Cypress Tests on Serverless' agents: enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 depends_on: - build diff --git a/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml b/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml index 6b72b5af240d2..fa8162ca93c0e 100644 --- a/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml +++ b/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml @@ -206,8 +206,6 @@ steps: imageProject: elastic-images-prod provider: gcp enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 depends_on: build timeout_in_minutes: 60 diff --git a/.buildkite/pipelines/on_merge.yml b/.buildkite/pipelines/on_merge.yml index c6900ccfe9c41..65c7f0095f063 100644 --- a/.buildkite/pipelines/on_merge.yml +++ b/.buildkite/pipelines/on_merge.yml @@ -39,7 +39,20 @@ steps: provider: gcp machineType: n2-highcpu-8 preemptible: true - key: quick_checks + timeout_in_minutes: 60 + retry: + automatic: + - exit_status: '-1' + limit: 3 + + - command: .buildkite/scripts/steps/checks.sh + label: 'Checks' + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-2 + preemptible: true timeout_in_minutes: 60 retry: automatic: @@ -54,7 +67,6 @@ steps: provider: gcp machineType: n2-standard-16 preemptible: true - key: linting timeout_in_minutes: 60 retry: automatic: @@ -69,8 +81,37 @@ steps: provider: gcp machineType: n2-standard-32 preemptible: true - key: linting_with_types - timeout_in_minutes: 90 + timeout_in_minutes: 60 + retry: + automatic: + - exit_status: '-1' + limit: 3 + + - command: .buildkite/scripts/steps/check_types.sh + label: 'Check Types' + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: c4-standard-4 + diskType: 'hyperdisk-balanced' + preemptible: true + spotZones: us-central1-a,us-central1-b,us-central1-c + timeout_in_minutes: 60 + retry: + automatic: + - exit_status: '-1' + limit: 3 + + - command: .buildkite/scripts/steps/checks/capture_oas_snapshot.sh + label: 'Check OAS Snapshot' + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + preemptible: true + timeout_in_minutes: 60 retry: automatic: - exit_status: '-1' @@ -136,11 +177,6 @@ steps: provider: gcp machineType: n2-standard-4 preemptible: true - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 3 retry: @@ -156,11 +192,6 @@ steps: provider: gcp machineType: n2-standard-4 preemptible: true - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 2 retry: @@ -176,11 +207,6 @@ steps: provider: gcp machineType: n2-standard-4 preemptible: true - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 8 retry: @@ -196,11 +222,6 @@ steps: provider: gcp machineType: n2-standard-4 preemptible: true - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 5 retry: @@ -216,11 +237,6 @@ steps: provider: gcp machineType: n2-standard-4 preemptible: true - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 1 retry: @@ -236,11 +252,6 @@ steps: provider: gcp machineType: n2-standard-4 preemptible: true - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 4 retry: @@ -256,11 +267,6 @@ steps: provider: gcp machineType: n2-standard-4 preemptible: true - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 6 retry: @@ -276,11 +282,6 @@ steps: provider: gcp machineType: n2-standard-4 preemptible: true - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 5 retry: @@ -296,11 +297,6 @@ steps: provider: gcp machineType: n2-standard-4 preemptible: true - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 6 retry: @@ -316,11 +312,6 @@ steps: provider: gcp machineType: n2-standard-4 preemptible: true - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 5 retry: @@ -336,11 +327,6 @@ steps: provider: gcp machineType: n2-standard-4 preemptible: true - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 6 retry: @@ -356,11 +342,6 @@ steps: provider: gcp machineType: n2-standard-4 preemptible: true - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 1 retry: @@ -376,11 +357,6 @@ steps: provider: gcp machineType: n2-standard-4 preemptible: true - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 1 retry: @@ -396,11 +372,6 @@ steps: provider: gcp machineType: n2-standard-4 preemptible: true - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 2 retry: @@ -416,11 +387,6 @@ steps: provider: gcp machineType: n2-standard-4 preemptible: true - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 2 retry: @@ -436,11 +402,6 @@ steps: provider: gcp machineType: n2-standard-4 preemptible: true - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 6 retry: @@ -456,11 +417,6 @@ steps: provider: gcp machineType: n2-standard-4 preemptible: true - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 8 retry: @@ -476,11 +432,6 @@ steps: provider: gcp machineType: n2-standard-4 preemptible: true - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 8 retry: @@ -495,14 +446,7 @@ steps: imageProject: elastic-images-prod provider: gcp enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 20 retry: @@ -517,14 +461,7 @@ steps: imageProject: elastic-images-prod provider: gcp enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 - depends_on: - - build - - quick_checks - - linting - - linting_with_types timeout_in_minutes: 60 parallelism: 14 retry: @@ -535,58 +472,11 @@ steps: - command: '.buildkite/scripts/steps/functional/on_merge_unsupported_ftrs.sh' label: Trigger unsupported ftr tests timeout_in_minutes: 10 - depends_on: - - build - - quick_checks - - linting - - linting_with_types - agents: - image: family/kibana-ubuntu-2004 - imageProject: elastic-images-prod - provider: gcp - machineType: n2-standard-2 - - - command: .buildkite/scripts/steps/checks.sh - label: 'Checks' - agents: - image: family/kibana-ubuntu-2004 - imageProject: elastic-images-prod - provider: gcp - machineType: n2-standard-2 - preemptible: true - timeout_in_minutes: 60 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - - command: .buildkite/scripts/steps/check_types.sh - label: 'Check Types' - agents: - image: family/kibana-ubuntu-2004 - imageProject: elastic-images-prod - provider: gcp - machineType: n2-standard-4 - preemptible: true - timeout_in_minutes: 70 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - - command: .buildkite/scripts/steps/checks/capture_oas_snapshot.sh - label: 'Check OAS Snapshot' agents: image: family/kibana-ubuntu-2004 imageProject: elastic-images-prod provider: gcp machineType: n2-standard-2 - preemptible: true - timeout_in_minutes: 60 - retry: - automatic: - - exit_status: '-1' - limit: 3 - command: .buildkite/scripts/steps/storybooks/build_and_upload.sh label: 'Build Storybooks' diff --git a/.buildkite/pipelines/pointer_compression.yml b/.buildkite/pipelines/pointer_compression.yml index 41598b3faed1f..29f0b75ca4184 100644 --- a/.buildkite/pipelines/pointer_compression.yml +++ b/.buildkite/pipelines/pointer_compression.yml @@ -362,8 +362,6 @@ steps: imageProject: elastic-images-prod provider: gcp enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 depends_on: - build @@ -381,8 +379,6 @@ steps: imageProject: elastic-images-prod provider: gcp enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 depends_on: - build diff --git a/.buildkite/pipelines/pull_request/apm_cypress.yml b/.buildkite/pipelines/pull_request/apm_cypress.yml index 9d2cca6d9d452..97935cc3489d1 100644 --- a/.buildkite/pipelines/pull_request/apm_cypress.yml +++ b/.buildkite/pipelines/pull_request/apm_cypress.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 120 parallelism: 1 # TODO: Set parallelism when apm_cypress handles it retry: diff --git a/.buildkite/pipelines/pull_request/base.yml b/.buildkite/pipelines/pull_request/base.yml index fdc80e6cb8595..e7b593f464b54 100644 --- a/.buildkite/pipelines/pull_request/base.yml +++ b/.buildkite/pipelines/pull_request/base.yml @@ -32,6 +32,18 @@ steps: - exit_status: '-1' limit: 3 + - command: .buildkite/scripts/steps/checks.sh + label: 'Checks' + key: checks + agents: + machineType: n2-standard-2 + preemptible: true + timeout_in_minutes: 60 + retry: + automatic: + - exit_status: '-1' + limit: 3 + - command: .buildkite/scripts/steps/lint.sh label: 'Linting' agents: @@ -50,7 +62,33 @@ steps: machineType: n2-standard-32 preemptible: true key: linting_with_types - timeout_in_minutes: 90 + timeout_in_minutes: 60 + retry: + automatic: + - exit_status: '-1' + limit: 3 + + - command: .buildkite/scripts/steps/checks/capture_oas_snapshot.sh + label: 'Check OAS Snapshot' + agents: + machineType: n2-standard-4 + preemptible: true + key: check_oas_snapshot + timeout_in_minutes: 60 + retry: + automatic: + - exit_status: '-1' + limit: 3 + + - command: .buildkite/scripts/steps/check_types.sh + label: 'Check Types' + agents: + machineType: c4-standard-4 + diskType: 'hyperdisk-balanced' + preemptible: true + spotZones: us-central1-a,us-central1-b,us-central1-c + key: check_types + timeout_in_minutes: 60 retry: automatic: - exit_status: '-1' @@ -85,41 +123,6 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/check_types.sh - label: 'Check Types' - agents: - machineType: n2-standard-4 - preemptible: true - key: check_types - timeout_in_minutes: 70 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - - command: .buildkite/scripts/steps/checks.sh - label: 'Checks' - key: checks - agents: - machineType: n2-standard-2 - preemptible: true - timeout_in_minutes: 60 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - - command: .buildkite/scripts/steps/checks/capture_oas_snapshot.sh - label: 'Check OAS Snapshot' - agents: - machineType: n2-standard-2 - preemptible: true - timeout_in_minutes: 60 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - command: .buildkite/scripts/steps/api_docs/build_api_docs.sh label: 'Build API Docs' agents: diff --git a/.buildkite/pipelines/pull_request/deploy_cloud.yml b/.buildkite/pipelines/pull_request/deploy_cloud.yml index e82d1ef2e494c..565c5af3bb0c1 100644 --- a/.buildkite/pipelines/pull_request/deploy_cloud.yml +++ b/.buildkite/pipelines/pull_request/deploy_cloud.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 30 soft_fail: true retry: diff --git a/.buildkite/pipelines/pull_request/exploratory_view_plugin.yml b/.buildkite/pipelines/pull_request/exploratory_view_plugin.yml index 42aaf59b1c1f2..05fc218080cd4 100644 --- a/.buildkite/pipelines/pull_request/exploratory_view_plugin.yml +++ b/.buildkite/pipelines/pull_request/exploratory_view_plugin.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 artifact_paths: - 'x-pack/plugins/observability_solution/exploratory_view/e2e/.journeys/**/*' diff --git a/.buildkite/pipelines/pull_request/fips.yml b/.buildkite/pipelines/pull_request/fips.yml index 3fa0ed9bd2062..4f906e4420c8f 100644 --- a/.buildkite/pipelines/pull_request/fips.yml +++ b/.buildkite/pipelines/pull_request/fips.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 soft_fail: true retry: diff --git a/.buildkite/pipelines/pull_request/fleet_cypress.yml b/.buildkite/pipelines/pull_request/fleet_cypress.yml index 071106209caaa..50912cc16e5a8 100644 --- a/.buildkite/pipelines/pull_request/fleet_cypress.yml +++ b/.buildkite/pipelines/pull_request/fleet_cypress.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 50 parallelism: 6 retry: diff --git a/.buildkite/pipelines/pull_request/inventory_cypress.yml b/.buildkite/pipelines/pull_request/inventory_cypress.yml index b1a8b999f09f2..3cd96de506288 100644 --- a/.buildkite/pipelines/pull_request/inventory_cypress.yml +++ b/.buildkite/pipelines/pull_request/inventory_cypress.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 120 parallelism: 1 retry: diff --git a/.buildkite/pipelines/pull_request/kbn_handlebars.yml b/.buildkite/pipelines/pull_request/kbn_handlebars.yml index ad338ec425a04..187058d238682 100644 --- a/.buildkite/pipelines/pull_request/kbn_handlebars.yml +++ b/.buildkite/pipelines/pull_request/kbn_handlebars.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 5 retry: automatic: diff --git a/.buildkite/pipelines/pull_request/observability_onboarding_cypress.yml b/.buildkite/pipelines/pull_request/observability_onboarding_cypress.yml index d0afe1cd138da..b0d438064d51e 100644 --- a/.buildkite/pipelines/pull_request/observability_onboarding_cypress.yml +++ b/.buildkite/pipelines/pull_request/observability_onboarding_cypress.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 120 retry: automatic: diff --git a/.buildkite/pipelines/pull_request/profiling_cypress.yml b/.buildkite/pipelines/pull_request/profiling_cypress.yml index 2b86cffe75fa6..8ed98a4fbc736 100644 --- a/.buildkite/pipelines/pull_request/profiling_cypress.yml +++ b/.buildkite/pipelines/pull_request/profiling_cypress.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 120 parallelism: 2 retry: diff --git a/.buildkite/pipelines/pull_request/response_ops.yml b/.buildkite/pipelines/pull_request/response_ops.yml index a5c9b27ee7ecf..9ac20e86f6660 100644 --- a/.buildkite/pipelines/pull_request/response_ops.yml +++ b/.buildkite/pipelines/pull_request/response_ops.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 120 parallelism: 9 retry: diff --git a/.buildkite/pipelines/pull_request/response_ops_cases.yml b/.buildkite/pipelines/pull_request/response_ops_cases.yml index 994fbb6c4963a..27289c864e2c1 100644 --- a/.buildkite/pipelines/pull_request/response_ops_cases.yml +++ b/.buildkite/pipelines/pull_request/response_ops_cases.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 120 retry: automatic: diff --git a/.buildkite/pipelines/pull_request/security_solution/ai_assistant.yml b/.buildkite/pipelines/pull_request/security_solution/ai_assistant.yml index 7f9a8d9da06e6..ca185a684d0c7 100644 --- a/.buildkite/pipelines/pull_request/security_solution/ai_assistant.yml +++ b/.buildkite/pipelines/pull_request/security_solution/ai_assistant.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 1 retry: @@ -24,8 +27,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 1 retry: diff --git a/.buildkite/pipelines/pull_request/security_solution/cloud_security_posture.yml b/.buildkite/pipelines/pull_request/security_solution/cloud_security_posture.yml index 93fad6eecf167..e1ba84bf4f661 100644 --- a/.buildkite/pipelines/pull_request/security_solution/cloud_security_posture.yml +++ b/.buildkite/pipelines/pull_request/security_solution/cloud_security_posture.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 1 retry: @@ -24,8 +27,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 1 retry: diff --git a/.buildkite/pipelines/pull_request/security_solution/cypress_burn.yml b/.buildkite/pipelines/pull_request/security_solution/cypress_burn.yml index 767f9d2c4745a..8b2e8e3d09f0d 100644 --- a/.buildkite/pipelines/pull_request/security_solution/cypress_burn.yml +++ b/.buildkite/pipelines/pull_request/security_solution/cypress_burn.yml @@ -3,14 +3,15 @@ steps: label: '[Soft fail] Defend Workflows Cypress Tests, burning changed specs' agents: enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 soft_fail: true parallelism: 1 @@ -21,14 +22,15 @@ steps: label: '[Soft fail] Defend Workflows Cypress Tests on Serverless, burning changed specs' agents: enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 soft_fail: true parallelism: 1 @@ -43,8 +45,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 1 retry: @@ -59,8 +64,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 50 soft_fail: true retry: diff --git a/.buildkite/pipelines/pull_request/security_solution/defend_workflows.yml b/.buildkite/pipelines/pull_request/security_solution/defend_workflows.yml index 9c3bb0e90a83a..7cffb9017b527 100644 --- a/.buildkite/pipelines/pull_request/security_solution/defend_workflows.yml +++ b/.buildkite/pipelines/pull_request/security_solution/defend_workflows.yml @@ -3,14 +3,15 @@ steps: label: 'Defend Workflows Cypress Tests' agents: enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 20 retry: @@ -22,14 +23,15 @@ steps: label: 'Defend Workflows Cypress Tests on Serverless' agents: enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 14 retry: diff --git a/.buildkite/pipelines/pull_request/security_solution/detection_engine.yml b/.buildkite/pipelines/pull_request/security_solution/detection_engine.yml index 34437d4136222..91b3ff7e6b7ff 100644 --- a/.buildkite/pipelines/pull_request/security_solution/detection_engine.yml +++ b/.buildkite/pipelines/pull_request/security_solution/detection_engine.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 5 retry: @@ -24,8 +27,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 2 retry: @@ -41,8 +47,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 5 retry: @@ -58,8 +67,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 2 retry: diff --git a/.buildkite/pipelines/pull_request/security_solution/entity_analytics.yml b/.buildkite/pipelines/pull_request/security_solution/entity_analytics.yml index bea72bf851345..d031c8f382a49 100644 --- a/.buildkite/pipelines/pull_request/security_solution/entity_analytics.yml +++ b/.buildkite/pipelines/pull_request/security_solution/entity_analytics.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 3 retry: @@ -24,8 +27,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 2 retry: diff --git a/.buildkite/pipelines/pull_request/security_solution/explore.yml b/.buildkite/pipelines/pull_request/security_solution/explore.yml index a3578c911c1cf..3805c8e37f905 100644 --- a/.buildkite/pipelines/pull_request/security_solution/explore.yml +++ b/.buildkite/pipelines/pull_request/security_solution/explore.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 2 retry: @@ -24,8 +27,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 2 retry: diff --git a/.buildkite/pipelines/pull_request/security_solution/investigations.yml b/.buildkite/pipelines/pull_request/security_solution/investigations.yml index 3b7fc869b4703..bcabf31f70e8b 100644 --- a/.buildkite/pipelines/pull_request/security_solution/investigations.yml +++ b/.buildkite/pipelines/pull_request/security_solution/investigations.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 7 retry: @@ -24,8 +27,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 8 retry: diff --git a/.buildkite/pipelines/pull_request/security_solution/osquery_cypress.yml b/.buildkite/pipelines/pull_request/security_solution/osquery_cypress.yml index e0b0278e3d969..c2de20aa5975d 100644 --- a/.buildkite/pipelines/pull_request/security_solution/osquery_cypress.yml +++ b/.buildkite/pipelines/pull_request/security_solution/osquery_cypress.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 8 retry: @@ -24,8 +27,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 8 retry: diff --git a/.buildkite/pipelines/pull_request/security_solution/playwright.yml b/.buildkite/pipelines/pull_request/security_solution/playwright.yml index 2efa342a5ce37..c92506297ea07 100644 --- a/.buildkite/pipelines/pull_request/security_solution/playwright.yml +++ b/.buildkite/pipelines/pull_request/security_solution/playwright.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 1 retry: @@ -24,8 +27,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 1 retry: diff --git a/.buildkite/pipelines/pull_request/security_solution/rule_management.yml b/.buildkite/pipelines/pull_request/security_solution/rule_management.yml index aee037704d332..e247dc2972620 100644 --- a/.buildkite/pipelines/pull_request/security_solution/rule_management.yml +++ b/.buildkite/pipelines/pull_request/security_solution/rule_management.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 5 retry: @@ -24,8 +27,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 1 retry: @@ -41,8 +47,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 4 retry: @@ -58,8 +67,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 parallelism: 2 retry: diff --git a/.buildkite/pipelines/pull_request/slo_plugin_e2e.yml b/.buildkite/pipelines/pull_request/slo_plugin_e2e.yml index 025c80809ab35..2cf1126cf1f5d 100644 --- a/.buildkite/pipelines/pull_request/slo_plugin_e2e.yml +++ b/.buildkite/pipelines/pull_request/slo_plugin_e2e.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 30 artifact_paths: - 'x-pack/plugins/observability_solution/slo/e2e/.journeys/**/*' diff --git a/.buildkite/pipelines/pull_request/synthetics_plugin.yml b/.buildkite/pipelines/pull_request/synthetics_plugin.yml index 0707650aa7c01..b4079b9fac307 100644 --- a/.buildkite/pipelines/pull_request/synthetics_plugin.yml +++ b/.buildkite/pipelines/pull_request/synthetics_plugin.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 artifact_paths: - 'x-pack/plugins/observability_solution/synthetics/e2e/.journeys/**/*' diff --git a/.buildkite/pipelines/pull_request/uptime_plugin.yml b/.buildkite/pipelines/pull_request/uptime_plugin.yml index 33a529739ae6f..4c1e05d7476fd 100644 --- a/.buildkite/pipelines/pull_request/uptime_plugin.yml +++ b/.buildkite/pipelines/pull_request/uptime_plugin.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 artifact_paths: - 'x-pack/plugins/observability_solution/synthetics/e2e/.journeys/**/*' diff --git a/.buildkite/pipelines/pull_request/ux_plugin_e2e.yml b/.buildkite/pipelines/pull_request/ux_plugin_e2e.yml index 977701cc99485..4bade14464f35 100644 --- a/.buildkite/pipelines/pull_request/ux_plugin_e2e.yml +++ b/.buildkite/pipelines/pull_request/ux_plugin_e2e.yml @@ -7,8 +7,11 @@ steps: depends_on: - build - quick_checks + - checks - linting - linting_with_types + - check_types + - check_oas_snapshot timeout_in_minutes: 60 artifact_paths: - 'x-pack/plugins/observability_solution/ux/e2e/.journeys/**/*' diff --git a/.buildkite/pipelines/security_solution_quality_gate/mki_periodic/mki_periodic_defend_workflows.yml b/.buildkite/pipelines/security_solution_quality_gate/mki_periodic/mki_periodic_defend_workflows.yml index 5795c8f61f30f..5106d619f95e5 100644 --- a/.buildkite/pipelines/security_solution_quality_gate/mki_periodic/mki_periodic_defend_workflows.yml +++ b/.buildkite/pipelines/security_solution_quality_gate/mki_periodic/mki_periodic_defend_workflows.yml @@ -10,8 +10,6 @@ steps: imageProject: elastic-images-prod provider: gcp enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 preemptible: true timeout_in_minutes: 300 @@ -32,8 +30,6 @@ steps: # imageProject: elastic-images-prod # provider: gcp # enableNestedVirtualization: true -# localSsds: 1 -# localSsdInterface: nvme # machineType: n2-standard-4 # timeout_in_minutes: 120 # retry: @@ -49,8 +45,6 @@ steps: # imageProject: elastic-images-prod # provider: gcp # enableNestedVirtualization: true -# localSsds: 1 -# localSsdInterface: nvme # machineType: n2-standard-4 # timeout_in_minutes: 120 # retry: @@ -66,8 +60,6 @@ steps: # imageProject: elastic-images-prod # provider: gcp # enableNestedVirtualization: true -# localSsds: 1 -# localSsdInterface: nvme # machineType: n2-standard-4 # timeout_in_minutes: 120 # retry: @@ -83,8 +75,6 @@ steps: # imageProject: elastic-images-prod # provider: gcp # enableNestedVirtualization: true -# localSsds: 1 -# localSsdInterface: nvme # machineType: n2-standard-4 # timeout_in_minutes: 120 # retry: @@ -100,8 +90,6 @@ steps: imageProject: elastic-images-prod provider: gcp enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 preemptible: true timeout_in_minutes: 120 @@ -118,8 +106,6 @@ steps: imageProject: elastic-images-prod provider: gcp enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 preemptible: true timeout_in_minutes: 120 @@ -136,8 +122,6 @@ steps: imageProject: elastic-images-prod provider: gcp enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 preemptible: true timeout_in_minutes: 120 @@ -157,8 +141,6 @@ steps: imageProject: elastic-images-prod provider: gcp enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 preemptible: true timeout_in_minutes: 300 diff --git a/.buildkite/pipelines/security_solution_quality_gate/mki_periodic/mki_periodic_detection_engine.yml b/.buildkite/pipelines/security_solution_quality_gate/mki_periodic/mki_periodic_detection_engine.yml index e25c6dfef0e4b..56b1904925f04 100644 --- a/.buildkite/pipelines/security_solution_quality_gate/mki_periodic/mki_periodic_detection_engine.yml +++ b/.buildkite/pipelines/security_solution_quality_gate/mki_periodic/mki_periodic_detection_engine.yml @@ -1,12 +1,12 @@ steps: - - group: "Cypress MKI - Detection Engine" + - group: 'Cypress MKI - Detection Engine' key: cypress_test_detections_engine steps: - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:detection_engine - label: "Cypress MKI - Detection Engine" + label: 'Cypress MKI - Detection Engine' key: test_detection_engine env: - BK_TEST_SUITE_KEY: "serverless-cypress-detection-engine" + BK_TEST_SUITE_KEY: 'serverless-cypress-detection-engine' agents: image: family/kibana-ubuntu-2004 imageProject: elastic-images-prod @@ -18,10 +18,10 @@ steps: parallelism: 8 - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:detection_engine:exceptions - label: "Cypress MKI - Detection Engine - Exceptions" + label: 'Cypress MKI - Detection Engine - Exceptions' key: test_detection_engine_exceptions env: - BK_TEST_SUITE_KEY: "serverless-cypress-detection-engine" + BK_TEST_SUITE_KEY: 'serverless-cypress-detection-engine' agents: image: family/kibana-ubuntu-2004 imageProject: elastic-images-prod @@ -32,7 +32,7 @@ steps: timeout_in_minutes: 300 parallelism: 6 - - group: "API MKI - Detection Engine - " + - group: 'API MKI - Detection Engine - ' key: api_test_detections_engine steps: - label: Running exception_lists_items:qa:serverless @@ -47,7 +47,7 @@ steps: timeout_in_minutes: 120 retry: automatic: - - exit_status: "1" + - exit_status: '1' limit: 2 - label: Running lists_items:qa:serverless @@ -62,7 +62,7 @@ steps: timeout_in_minutes: 120 retry: automatic: - - exit_status: "1" + - exit_status: '1' limit: 2 - label: Running user_roles:qa:serverless @@ -77,7 +77,7 @@ steps: timeout_in_minutes: 120 retry: automatic: - - exit_status: "1" + - exit_status: '1' limit: 2 - label: Running telemetry:qa:serverless @@ -92,7 +92,7 @@ steps: timeout_in_minutes: 120 retry: automatic: - - exit_status: "1" + - exit_status: '1' limit: 2 - label: Running exception_workflows:essentials:qa:serverless @@ -107,12 +107,12 @@ steps: timeout_in_minutes: 120 retry: automatic: - - exit_status: "1" + - exit_status: '1' limit: 2 - - label: Running exception_operators_date_numeric_types:essentials:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh exception_operators_date_numeric_types:essentials:qa:serverless - key: exception_operators_date_numeric_types:essentials:qa:serverless + - label: Running exception_operators_date_types:essentials:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh exception_operators_date_types:essentials:qa:serverless + key: exception_operators_date_types:essentials:qa:serverless agents: image: family/kibana-ubuntu-2004 imageProject: elastic-images-prod @@ -122,7 +122,52 @@ steps: timeout_in_minutes: 120 retry: automatic: - - exit_status: "1" + - exit_status: '1' + limit: 2 + + - label: Running exception_operators_double:essentials:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh exception_operators_double:essentials:qa:serverless + key: exception_operators_double:essentials:qa:serverless + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + preemptible: true + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '1' + limit: 2 + + - label: Running exception_operators_float:essentials:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh exception_operators_float:essentials:qa:serverless + key: exception_operators_float:essentials:qa:serverless + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + preemptible: true + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '1' + limit: 2 + + - label: Running exception_operators_integer:essentials:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh exception_operators_integer:essentials:qa:serverless + key: exception_operators_integer:essentials:qa:serverless + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + preemptible: true + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '1' limit: 2 - label: Running exception_operators_keyword:essentials:qa:serverless @@ -137,7 +182,7 @@ steps: timeout_in_minutes: 120 retry: automatic: - - exit_status: "1" + - exit_status: '1' limit: 2 - label: Running exception_operators_ips:essentials:qa:serverless @@ -152,7 +197,7 @@ steps: timeout_in_minutes: 120 retry: automatic: - - exit_status: "1" + - exit_status: '1' limit: 2 - label: Running exception_operators_long:essentials:qa:serverless @@ -167,7 +212,7 @@ steps: timeout_in_minutes: 120 retry: automatic: - - exit_status: "1" + - exit_status: '1' limit: 2 - label: Running exception_operators_text:essentials:qa:serverless @@ -182,7 +227,7 @@ steps: timeout_in_minutes: 120 retry: automatic: - - exit_status: "1" + - exit_status: '1' limit: 2 - label: Running actions:qa:serverless @@ -197,7 +242,7 @@ steps: timeout_in_minutes: 120 retry: automatic: - - exit_status: "1" + - exit_status: '1' limit: 2 - label: Running alerts:qa:serverless @@ -212,7 +257,7 @@ steps: timeout_in_minutes: 120 retry: automatic: - - exit_status: "1" + - exit_status: '1' limit: 2 - label: Running alerts:essentials:qa:serverless @@ -227,12 +272,117 @@ steps: timeout_in_minutes: 120 retry: automatic: - - exit_status: "1" + - exit_status: '1' + limit: 2 + + - label: Running rule_execution_logic:eql:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_execution_logic:eql:qa:serverless + key: rule_execution_logic:eql:qa:serverless + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + preemptible: true + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '1' + limit: 2 + + - label: Running rule_execution_logic:esql:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_execution_logic:esql:qa:serverless + key: rule_execution_logic:esql:qa:serverless + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + preemptible: true + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '1' + limit: 2 + + - label: Running rule_execution_logic:general_logic:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_execution_logic:general_logic:qa:serverless + key: rule_execution_logic:general_logic:qa:serverless + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + preemptible: true + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '1' + limit: 2 + + - label: Running rule_execution_logic:indicator_match:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_execution_logic:indicator_match:qa:serverless + key: rule_execution_logic:indicator_match:qa:serverless + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + preemptible: true + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '1' + limit: 2 + + - label: Running rule_execution_logic:machine_learning:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_execution_logic:machine_learning:qa:serverless + key: rule_execution_logic:machine_learning:qa:serverless + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + preemptible: true + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '1' + limit: 2 + + - label: Running rule_execution_logic:new_terms:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_execution_logic:new_terms:qa:serverless + key: rule_execution_logic:new_terms:qa:serverless + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + preemptible: true + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '1' + limit: 2 + + - label: Running rule_execution_logic:query:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_execution_logic:query:qa:serverless + key: rule_execution_logic:query:qa:serverless + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + preemptible: true + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '1' limit: 2 - - label: Running rule_execution_logic:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_execution_logic:qa:serverless - key: rule_execution_logic:qa:serverless + - label: Running rule_execution_logic:threshold:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_execution_logic:threshold:qa:serverless + key: rule_execution_logic:threshold:qa:serverless agents: image: family/kibana-ubuntu-2004 imageProject: elastic-images-prod @@ -242,5 +392,5 @@ steps: timeout_in_minutes: 120 retry: automatic: - - exit_status: "1" + - exit_status: '1' limit: 2 diff --git a/.buildkite/pipelines/security_solution_quality_gate/mki_quality_gate/mki_quality_gate_defend_workflows.yml b/.buildkite/pipelines/security_solution_quality_gate/mki_quality_gate/mki_quality_gate_defend_workflows.yml index e59ca507e4003..3d30e78583409 100644 --- a/.buildkite/pipelines/security_solution_quality_gate/mki_quality_gate/mki_quality_gate_defend_workflows.yml +++ b/.buildkite/pipelines/security_solution_quality_gate/mki_quality_gate/mki_quality_gate_defend_workflows.yml @@ -7,8 +7,6 @@ steps: imageProject: elastic-images-prod provider: gcp enableNestedVirtualization: true - localSsds: 1 - localSsdInterface: nvme machineType: n2-standard-4 timeout_in_minutes: 300 parallelism: 1 diff --git a/.buildkite/pipelines/security_solution_quality_gate/mki_quality_gate/mki_quality_gate_detection_engine.yml b/.buildkite/pipelines/security_solution_quality_gate/mki_quality_gate/mki_quality_gate_detection_engine.yml index 90c90ae8a3a36..023099dd99392 100644 --- a/.buildkite/pipelines/security_solution_quality_gate/mki_quality_gate/mki_quality_gate_detection_engine.yml +++ b/.buildkite/pipelines/security_solution_quality_gate/mki_quality_gate/mki_quality_gate_detection_engine.yml @@ -103,9 +103,51 @@ steps: - exit_status: "1" limit: 2 - - label: Running exception_operators_date_numeric_types:essentials:qa:serverless:release - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh exception_operators_date_numeric_types:essentials:qa:serverless:release - key: exception_operators_date_numeric_types:essentials:qa:serverless:release + - label: Running exception_operators_date_types:essentials:qa:serverless:release + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh exception_operators_date_types:essentials:qa:serverless:release + key: exception_operators_date_types:essentials:qa:serverless:release + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 + + - label: Running exception_operators_double:essentials:qa:serverless:release + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh exception_operators_double:essentials:qa:serverless:release + key: exception_operators_double:essentials:qa:serverless:release + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 + + - label: Running exception_operators_float:essentials:qa:serverless:release + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh exception_operators_float:essentials:qa:serverless:release + key: exception_operators_float:essentials:qa:serverless:release + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 + + - label: Running exception_operators_integer:essentials:qa:serverless:release + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh exception_operators_integer:essentials:qa:serverless:release + key: exception_operators_integer:essentials:qa:serverless:release agents: image: family/kibana-ubuntu-2004 imageProject: elastic-images-prod @@ -215,9 +257,107 @@ steps: - exit_status: "1" limit: 2 - - label: Running rule_execution_logic:qa:serverless:release - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_execution_logic:qa:serverless:release - key: rule_execution_logic:qa:serverless:release + - label: Running rule_execution_logic:eql:qa:serverless:release + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_execution_logic:eql:qa:serverless:release + key: rule_execution_logic:eql:qa:serverless:release + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 + + - label: Running rule_execution_logic:esql:qa:serverless:release + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_execution_logic:esql:qa:serverless:release + key: rule_execution_logic:esql:qa:serverless:release + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 + + - label: Running rule_execution_logic:general_logic:qa:serverless:release + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_execution_logic:general_logic:qa:serverless:release + key: rule_execution_logic:general_logic:qa:serverless:release + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 + + - label: Running rule_execution_logic:indicator_match:qa:serverless:release + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_execution_logic:indicator_match:qa:serverless:release + key: rule_execution_logic:indicator_match:qa:serverless:release + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 + + - label: Running rule_execution_logic:machine_learning:qa:serverless:release + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_execution_logic:machine_learning:qa:serverless:release + key: rule_execution_logic:machine_learning:qa:serverless:release + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 + + - label: Running rule_execution_logic:new_terms:qa:serverless:release + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_execution_logic:new_terms:qa:serverless:release + key: rule_execution_logic:new_terms:qa:serverless:release + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 + + - label: Running rule_execution_logic:query:qa:serverless:release + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_execution_logic:query:qa:serverless:release + key: rule_execution_logic:query:qa:serverless:release + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-4 + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 + + - label: Running rule_execution_logic:threshold:qa:serverless:release + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_execution_logic:threshold:qa:serverless:release + key: rule_execution_logic:threshold:qa:serverless:release agents: image: family/kibana-ubuntu-2004 imageProject: elastic-images-prod diff --git a/.buildkite/pull_requests.json b/.buildkite/pull_requests.json index cbc0e9df03dc8..e88982ec00d9d 100644 --- a/.buildkite/pull_requests.json +++ b/.buildkite/pull_requests.json @@ -14,6 +14,7 @@ "build_on_commit": true, "build_on_comment": true, "build_drafts": false, + "build_on_ready": true, "trigger_comment_regex": "^(?:(?:buildkite\\W+)?(?:build|test)\\W+(?:this|it))|^\\/ci$", "always_trigger_comment_regex": "^(?:(?:buildkite\\W+)?(?:build|test)\\W+(?:this|it))|^\\/ci$", "skip_ci_labels": ["skip-ci"], diff --git a/.buildkite/scripts/bootstrap.sh b/.buildkite/scripts/bootstrap.sh index b7576dda72f24..a126ad9132696 100755 --- a/.buildkite/scripts/bootstrap.sh +++ b/.buildkite/scripts/bootstrap.sh @@ -12,12 +12,18 @@ if [[ "${BOOTSTRAP_ALWAYS_FORCE_INSTALL:-}" ]]; then BOOTSTRAP_PARAMS+=(--force-install) fi -# Use the node_modules that is baked into the agent image, if it exists, as a cache +# Use the packages that are baked into the agent image, if they exist, as a cache # But only for agents not mounting the workspace on a local ssd or in memory # It actually ends up being slower to move all of the tiny files between the disks vs extracting archives from the yarn cache -if [[ -d ~/.kibana/node_modules && "$(pwd)" != *"/local-ssd/"* && "$(pwd)" != "/dev/shm"* ]]; then - echo "Using ~/.kibana/node_modules as a starting point" - mv ~/.kibana/node_modules ./ +if [[ "$(pwd)" != *"/local-ssd/"* && "$(pwd)" != "/dev/shm"* ]]; then + if [[ -d ~/.kibana/node_modules ]]; then + echo "Using ~/.kibana/node_modules as a starting point" + mv ~/.kibana/node_modules ./ + fi + if [[ -d ~/.kibana/.yarn-local-mirror ]]; then + echo "Using ~/.kibana/.yarn-local-mirror as a starting point" + mv ~/.kibana/.yarn-local-mirror ./ + fi fi if ! yarn kbn bootstrap "${BOOTSTRAP_PARAMS[@]}"; then diff --git a/.buildkite/scripts/common/setup_node.sh b/.buildkite/scripts/common/setup_node.sh index c6fbfeaee51bc..aac3d26702691 100755 --- a/.buildkite/scripts/common/setup_node.sh +++ b/.buildkite/scripts/common/setup_node.sh @@ -10,7 +10,6 @@ NODE_VERSION="$(cat "$KIBANA_DIR/.node-version")" export NODE_VERSION export NODE_DIR="$CACHE_DIR/node/$NODE_VERSION" export NODE_BIN_DIR="$NODE_DIR/bin" -export YARN_OFFLINE_CACHE="$CACHE_DIR/yarn-offline-cache" ## Install node for whatever the current os/arch are hostArch="$(command uname -m)" @@ -77,8 +76,6 @@ if [[ ! $(which yarn) || $(yarn --version) != "$YARN_VERSION" ]]; then npm_install_global yarn "^$YARN_VERSION" fi -yarn config set yarn-offline-mirror "$YARN_OFFLINE_CACHE" - YARN_GLOBAL_BIN=$(yarn global bin) export YARN_GLOBAL_BIN export PATH="$PATH:$YARN_GLOBAL_BIN" diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.ts b/.buildkite/scripts/pipelines/pull_request/pipeline.ts index 722c30cb42534..6b805d540c254 100644 --- a/.buildkite/scripts/pipelines/pull_request/pipeline.ts +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.ts @@ -255,18 +255,43 @@ const getPipeline = (filename: string, removeSteps = true) => { if ( (await doAnyChangesMatch([ /^package.json/, + /^packages\/kbn-discover-utils/, /^packages\/kbn-doc-links/, + /^packages\/kbn-dom-drag-drop/, /^packages\/kbn-es-query/, - /^packages\/kbn-i18n-react/, /^packages\/kbn-i18n/, + /^packages\/kbn-i18n-react/, /^packages\/kbn-expandable-flyout/, + /^packages\/kbn-grouping/, + /^packages\/kbn-resizable-layout/, + /^packages\/kbn-rison/, + /^packages\/kbn-rule-data-utils/, + /^packages\/kbn-safer-lodash-set/, + /^packages\/kbn-search-types/, /^packages\/kbn-securitysolution-.*/, + /^packages\/kbn-securitysolution-ecs/, + /^packages\/kbn-securitysolution-io-ts-alerting-types/, /^packages\/kbn-securitysolution-io-ts-list-types/, + /^packages\/kbn-securitysolution-list-hooks/, + /^packages\/kbn-securitysolution-t-grid/, + /^packages\/kbn-ui-theme/, + /^packages\/kbn-utility-types/, + /^packages\/react/, /^packages\/shared-ux/, /^src\/core/, + /^src\/plugins\/charts/, + /^src\/plugins\/controls/, /^src\/plugins\/data/, - /^src\/plugins\/kibana_utils/, + /^src\/plugins\/data_views/, + /^src\/plugins\/discover/, + /^src\/plugins\/field_formats/, /^src\/plugins\/inspector/, + /^src\/plugins\/kibana_react/, + /^src\/plugins\/kibana_utils/, + /^src\/plugins\/saved_search/, + /^src\/plugins\/ui_actions/, + /^src\/plugins\/unified_histogram/, + /^src\/plugins\/unified_search/, /^x-pack\/packages\/kbn-elastic-assistant/, /^x-pack\/packages\/kbn-elastic-assistant-common/, /^x-pack\/packages\/security-solution/, @@ -282,7 +307,7 @@ const getPipeline = (filename: string, removeSteps = true) => { /^x-pack\/plugins\/task_manager/, /^x-pack\/plugins\/threat_intelligence/, /^x-pack\/plugins\/timelines/, - /^x-pack\/plugins\/triggers_actions_ui\/public\/application\/sections\/alerts_table/, + /^x-pack\/plugins\/triggers_actions_ui/, /^x-pack\/plugins\/usage_collection\/public/, /^x-pack\/test\/functional\/es_archives\/security_solution/, /^x-pack\/test\/security_solution_cypress/, diff --git a/.buildkite/scripts/steps/checks.sh b/.buildkite/scripts/steps/checks.sh index 8d62a305dd535..ce7dec4f36e8d 100755 --- a/.buildkite/scripts/steps/checks.sh +++ b/.buildkite/scripts/steps/checks.sh @@ -4,6 +4,7 @@ set -euo pipefail export DISABLE_BOOTSTRAP_VALIDATION=false .buildkite/scripts/bootstrap.sh +.buildkite/scripts/copy_es_snapshot_cache.sh if [[ "${FTR_ENABLE_FIPS_AGENT:-}" == "true" ]]; then .buildkite/scripts/steps/checks/verify_fips_enabled.sh diff --git a/.buildkite/scripts/steps/checks/i18n.sh b/.buildkite/scripts/steps/checks/i18n.sh index 090512e391d7c..f23ed2d63759e 100755 --- a/.buildkite/scripts/steps/checks/i18n.sh +++ b/.buildkite/scripts/steps/checks/i18n.sh @@ -5,4 +5,4 @@ set -euo pipefail source .buildkite/scripts/common/util.sh echo --- Check i18n -node scripts/i18n_check +node scripts/i18n_check --quiet diff --git a/.buildkite/scripts/steps/cloud/build_and_deploy.sh b/.buildkite/scripts/steps/cloud/build_and_deploy.sh index 220ab497aaf7b..e2a55b03ebf64 100755 --- a/.buildkite/scripts/steps/cloud/build_and_deploy.sh +++ b/.buildkite/scripts/steps/cloud/build_and_deploy.sh @@ -19,7 +19,7 @@ download_artifact "kibana-$VERSION-linux-x86_64.tar.gz" ./target --build "${KIBA echo "--- Build Cloud Distribution" ELASTICSEARCH_MANIFEST_URL="https://storage.googleapis.com/kibana-ci-es-snapshots-daily/$(jq -r '.version' package.json)/manifest-latest-verified.json" ELASTICSEARCH_SHA=$(curl -s $ELASTICSEARCH_MANIFEST_URL | jq -r '.sha') -ELASTICSEARCH_CLOUD_IMAGE="docker.elastic.co/kibana-ci/elasticsearch-cloud:$VERSION-$ELASTICSEARCH_SHA" +ELASTICSEARCH_CLOUD_IMAGE="docker.elastic.co/kibana-ci/elasticsearch-cloud-ess:$VERSION-$ELASTICSEARCH_SHA" KIBANA_CLOUD_IMAGE="docker.elastic.co/kibana-ci/kibana-cloud:$VERSION-$GIT_COMMIT" CLOUD_DEPLOYMENT_NAME="kibana-pr-$BUILDKITE_PULL_REQUEST" diff --git a/.buildkite/scripts/steps/cloud/deploy.json b/.buildkite/scripts/steps/cloud/deploy.json index 3080f083aadfd..5ee605c39ff5c 100644 --- a/.buildkite/scripts/steps/cloud/deploy.json +++ b/.buildkite/scripts/steps/cloud/deploy.json @@ -7,7 +7,7 @@ "plan": { "cluster_topology": [ { - "instance_configuration_id": "gcp.integrationsserver.1", + "instance_configuration_id": "gcp.integrationsserver.n2.68x32x45", "zone_count": 1, "size": { "resource": "memory", @@ -32,7 +32,7 @@ "cluster_topology": [ { "zone_count": 1, - "instance_configuration_id": "gcp.coordinating.1", + "instance_configuration_id": "gcp.es.coordinating.n2.68x16x45", "node_roles": [ "ingest", "remote_cluster_client" @@ -50,7 +50,7 @@ "data": "hot" } }, - "instance_configuration_id": "gcp.data.highio.1", + "instance_configuration_id": "gcp.es.datahot.n2.68x32x45", "node_roles": [ "master", "ingest", @@ -72,7 +72,7 @@ "data": "warm" } }, - "instance_configuration_id": "gcp.data.highstorage.1", + "instance_configuration_id": "gcp.es.datawarm.n2.68x10x190", "node_roles": [ "data_warm", "remote_cluster_client" @@ -90,7 +90,7 @@ "data": "cold" } }, - "instance_configuration_id": "gcp.data.highstorage.1", + "instance_configuration_id": "gcp.es.datacold.n2.68x10x190", "node_roles": [ "data_cold", "remote_cluster_client" @@ -108,7 +108,7 @@ "data": "frozen" } }, - "instance_configuration_id": "gcp.es.datafrozen.n1.64x10x95", + "instance_configuration_id": "gcp.es.datafrozen.n2.68x10x90", "node_roles": [ "data_frozen" ], @@ -120,7 +120,7 @@ }, { "zone_count": 1, - "instance_configuration_id": "gcp.master.1", + "instance_configuration_id": "gcp.es.master.n2.68x32x45", "node_roles": [ "master", "remote_cluster_client" @@ -142,7 +142,7 @@ }, "autoscaling_tier_override": true, "id": "ml", - "instance_configuration_id": "gcp.ml.1", + "instance_configuration_id": "gcp.es.ml.n2.68x32x45", "node_roles": [ "ml", "remote_cluster_client" @@ -155,7 +155,7 @@ "enabled_built_in_plugins": [] }, "deployment_template": { - "id": "gcp-io-optimized-v2" + "id": "gcp-cpu-optimized" } }, "ref_id": "main-elasticsearch" @@ -168,7 +168,7 @@ "plan": { "cluster_topology": [ { - "instance_configuration_id": "gcp.kibana.1", + "instance_configuration_id": "gcp.kibana.n2.68x32x45", "zone_count": 1, "size": { "value": 2048, diff --git a/.buildkite/scripts/steps/es_snapshots/build.sh b/.buildkite/scripts/steps/es_snapshots/build.sh index d9d685338250f..0485763b0e918 100755 --- a/.buildkite/scripts/steps/es_snapshots/build.sh +++ b/.buildkite/scripts/steps/es_snapshots/build.sh @@ -85,11 +85,11 @@ echo "--- Create kibana-ci docker cloud image archives" # When we bump versions, these dependencies may not exist yet, but we don't want to # block the rest of the snapshot promotion process set +e -./gradlew :distribution:docker:cloud-docker-export:assemble && { - ES_CLOUD_ID=$(docker images "docker.elastic.co/elasticsearch-ci/elasticsearch-cloud" --format "{{.ID}}") - ES_CLOUD_VERSION=$(docker images "docker.elastic.co/elasticsearch-ci/elasticsearch-cloud" --format "{{.Tag}}") +./gradlew :distribution:docker:cloud-ess-docker-export:assemble && { + ES_CLOUD_ID=$(docker images "docker.elastic.co/elasticsearch/elasticsearch-cloud-ess" --format "{{.ID}}") + ES_CLOUD_VERSION=$(docker images "docker.elastic.co/elasticsearch/elasticsearch-cloud-ess" --format "{{.Tag}}") KIBANA_ES_CLOUD_VERSION="$ES_CLOUD_VERSION-$ELASTICSEARCH_GIT_COMMIT" - KIBANA_ES_CLOUD_IMAGE="docker.elastic.co/kibana-ci/elasticsearch-cloud:$KIBANA_ES_CLOUD_VERSION" + KIBANA_ES_CLOUD_IMAGE="docker.elastic.co/kibana-ci/elasticsearch-cloud-ess:$KIBANA_ES_CLOUD_VERSION" echo $ES_CLOUD_ID $ES_CLOUD_VERSION $KIBANA_ES_CLOUD_VERSION $KIBANA_ES_CLOUD_IMAGE docker tag "$ES_CLOUD_ID" "$KIBANA_ES_CLOUD_IMAGE" diff --git a/.buildkite/scripts/steps/functional/defend_workflows_burn.sh b/.buildkite/scripts/steps/functional/defend_workflows_burn.sh index 643a8d9f4ec53..6a97ba4e82b33 100644 --- a/.buildkite/scripts/steps/functional/defend_workflows_burn.sh +++ b/.buildkite/scripts/steps/functional/defend_workflows_burn.sh @@ -5,6 +5,7 @@ set -euo pipefail source .buildkite/scripts/steps/functional/common.sh .buildkite/scripts/bootstrap.sh +.buildkite/scripts/copy_es_snapshot_cache.sh node scripts/build_kibana_platform_plugins.js export JOB=kibana-defend-workflows-cypress diff --git a/.buildkite/scripts/steps/functional/defend_workflows_serverless_burn.sh b/.buildkite/scripts/steps/functional/defend_workflows_serverless_burn.sh index 3f85a9b051845..4bebee15953e6 100644 --- a/.buildkite/scripts/steps/functional/defend_workflows_serverless_burn.sh +++ b/.buildkite/scripts/steps/functional/defend_workflows_serverless_burn.sh @@ -5,6 +5,7 @@ set -euo pipefail source .buildkite/scripts/steps/functional/common.sh .buildkite/scripts/bootstrap.sh +.buildkite/scripts/copy_es_snapshot_cache.sh node scripts/build_kibana_platform_plugins.js export JOB=kibana-defend-workflows-serverless-cypress diff --git a/.buildkite/scripts/steps/functional/exploratory_view_plugin.sh b/.buildkite/scripts/steps/functional/exploratory_view_plugin.sh index d14033883312f..adee8986bc746 100755 --- a/.buildkite/scripts/steps/functional/exploratory_view_plugin.sh +++ b/.buildkite/scripts/steps/functional/exploratory_view_plugin.sh @@ -6,6 +6,7 @@ source .buildkite/scripts/common/util.sh .buildkite/scripts/bootstrap.sh .buildkite/scripts/download_build_artifacts.sh +.buildkite/scripts/copy_es_snapshot_cache.sh export JOB=kibana-observability-plugin diff --git a/.buildkite/scripts/steps/functional/slo_plugin_e2e.sh b/.buildkite/scripts/steps/functional/slo_plugin_e2e.sh index 95007fbff85bf..0492e41ae7041 100755 --- a/.buildkite/scripts/steps/functional/slo_plugin_e2e.sh +++ b/.buildkite/scripts/steps/functional/slo_plugin_e2e.sh @@ -6,6 +6,7 @@ source .buildkite/scripts/common/util.sh .buildkite/scripts/bootstrap.sh .buildkite/scripts/download_build_artifacts.sh +.buildkite/scripts/copy_es_snapshot_cache.sh export JOB=kibana-ux-plugin-synthetics diff --git a/.buildkite/scripts/steps/functional/synthetics_plugin.sh b/.buildkite/scripts/steps/functional/synthetics_plugin.sh index 5ad02174ccd26..3e31b92011ad2 100755 --- a/.buildkite/scripts/steps/functional/synthetics_plugin.sh +++ b/.buildkite/scripts/steps/functional/synthetics_plugin.sh @@ -6,6 +6,7 @@ source .buildkite/scripts/common/util.sh .buildkite/scripts/bootstrap.sh .buildkite/scripts/download_build_artifacts.sh +.buildkite/scripts/copy_es_snapshot_cache.sh export JOB=kibana-synthetics-plugin diff --git a/.buildkite/scripts/steps/functional/uptime_plugin.sh b/.buildkite/scripts/steps/functional/uptime_plugin.sh index 3122953862c73..b4cdd0fb5738a 100755 --- a/.buildkite/scripts/steps/functional/uptime_plugin.sh +++ b/.buildkite/scripts/steps/functional/uptime_plugin.sh @@ -6,6 +6,7 @@ source .buildkite/scripts/common/util.sh .buildkite/scripts/bootstrap.sh .buildkite/scripts/download_build_artifacts.sh +.buildkite/scripts/copy_es_snapshot_cache.sh export JOB=kibana-uptime-plugin diff --git a/.buildkite/scripts/steps/functional/ux_synthetics_e2e.sh b/.buildkite/scripts/steps/functional/ux_synthetics_e2e.sh index dbb3289f604e5..bcc5b71149607 100755 --- a/.buildkite/scripts/steps/functional/ux_synthetics_e2e.sh +++ b/.buildkite/scripts/steps/functional/ux_synthetics_e2e.sh @@ -6,6 +6,7 @@ source .buildkite/scripts/common/util.sh .buildkite/scripts/bootstrap.sh .buildkite/scripts/download_build_artifacts.sh +.buildkite/scripts/copy_es_snapshot_cache.sh export JOB=kibana-ux-plugin-synthetics diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1451c647f658e..43a315646772d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -525,6 +525,7 @@ x-pack/plugins/index_management @elastic/kibana-management x-pack/packages/index-management/index_management_shared_types @elastic/kibana-management test/plugin_functional/plugins/index_patterns @elastic/kibana-data-discovery x-pack/packages/ml/inference_integration_flyout @elastic/ml-ui +x-pack/packages/ai-infra/inference-common @elastic/appex-ai-infra x-pack/plugins/inference @elastic/appex-ai-infra x-pack/packages/kbn-infra-forge @elastic/obs-ux-management-team x-pack/plugins/observability_solution/infra @elastic/obs-ux-logs-team @elastic/obs-ux-infra_services-team @@ -753,7 +754,7 @@ src/plugins/saved_objects_finder @elastic/kibana-data-discovery test/plugin_functional/plugins/saved_objects_hidden_from_http_apis_type @elastic/kibana-core test/plugin_functional/plugins/saved_objects_hidden_type @elastic/kibana-core src/plugins/saved_objects_management @elastic/kibana-core -src/plugins/saved_objects @elastic/kibana-core +src/plugins/saved_objects @elastic/appex-sharedux packages/kbn-saved-objects-settings @elastic/appex-sharedux src/plugins/saved_objects_tagging_oss @elastic/appex-sharedux x-pack/plugins/saved_objects_tagging @elastic/appex-sharedux @@ -792,7 +793,6 @@ 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/packages/security/role_management_model @elastic/kibana-security -x-pack/packages/security-solution/common @elastic/security-threat-hunting-investigations x-pack/packages/security-solution/distribution_bar @elastic/kibana-cloud-security-posture x-pack/plugins/security_solution_ess @elastic/security-solution x-pack/packages/security-solution/features @elastic/security-threat-hunting-explore @@ -954,7 +954,7 @@ packages/kbn-triggers-actions-ui-types @elastic/response-ops packages/kbn-try-in-console @elastic/search-kibana packages/kbn-ts-projects @elastic/kibana-operations packages/kbn-ts-type-check-cli @elastic/kibana-operations -packages/kbn-typed-react-router-config @elastic/obs-knowledge-team @elastic/obs-ux-management-team +packages/kbn-typed-react-router-config @elastic/obs-knowledge-team @elastic/obs-ux-infra_services-team packages/kbn-ui-actions-browser @elastic/appex-sharedux x-pack/examples/ui_actions_enhanced_examples @elastic/appex-sharedux src/plugins/ui_actions_enhanced @elastic/appex-sharedux @@ -1023,7 +1023,7 @@ packages/kbn-zod-helpers @elastic/security-detection-rule-management # The #CC# prefix delineates Code Coverage, # used for the 'team' designator within Kibana Stats -/x-pack/test/api_integration/apis/metrics_ui @elastic/obs-ux-logs-team @elastic/obs-ux-infra_services-team +/x-pack/test/api_integration/apis/metrics_ui @elastic/obs-ux-infra_services-team x-pack/test_serverless/api_integration/test_suites/common/platform_security @elastic/kibana-security # Observability Entities Team (@elastic/obs-entities) @@ -1034,6 +1034,8 @@ x-pack/test_serverless/api_integration/test_suites/common/platform_security @ela /x-pack/test/api_integration/apis/entity_manager @elastic/obs-entities # Data Discovery +/test/plugin_functional/plugins/data_search @elastic/kibana-data-discovery +/test/plugin_functional/plugins/index_patterns @elastic/kibana-data-discovery /x-pack/test/api_integration/apis/kibana/kql_telemetry @elastic/kibana-data-discovery @elastic/kibana-visualizations /x-pack/test_serverless/functional/es_archives/pre_calculated_histogram @elastic/kibana-data-discovery /x-pack/test_serverless/functional/es_archives/kibana_sample_data_flights_index_pattern @elastic/kibana-data-discovery @@ -1143,15 +1145,19 @@ packages/kbn-monaco/src/esql @elastic/kibana-esql #CC# /x-pack/plugins/reporting/ @elastic/appex-sharedux #CC# /x-pack/plugins/security_solution_serverless/ @elastic/appex-sharedux -### Observability Plugins +# Observability UI +/x-pack/test_serverless/api_integration/test_suites/observability/config.ts @elastic/observability-ui @elastic/appex-qa +/x-pack/test_serverless/api_integration/test_suites/observability/index.ts @elastic/observability-ui + +### Observability Plugins # Observability AI Assistant x-pack/test/observability_ai_assistant_api_integration @elastic/obs-ai-assistant x-pack/test/observability_ai_assistant_functional @elastic/obs-ai-assistant x-pack/test_serverless/**/test_suites/observability/ai_assistant @elastic/obs-ai-assistant -# Infra Monitoring +# Infra Obs ## This plugin mostly contains the codebase for the infra services, but also includes some code for the Logs UI app. ## To keep @elastic/obs-ux-logs-team as codeowner of the plugin manifest without requiring a review for all the other code changes ## the priority on codeownership will be as follow: @@ -1161,6 +1167,7 @@ x-pack/test_serverless/**/test_suites/observability/ai_assistant @elastic/obs-ai ## This should allow the infra team to work without dependencies on the @elastic/obs-ux-logs-team, which will maintain ownership of the Logs UI code only. ## infra/{common,docs,public,server}/{sub-folders}/ -> @elastic/obs-ux-infra_services-team +/test/common/plugins/otel_metrics @elastic/obs-ux-infra_services-team /x-pack/plugins/observability_solution/infra/common @elastic/obs-ux-infra_services-team /x-pack/plugins/observability_solution/infra/docs @elastic/obs-ux-infra_services-team /x-pack/plugins/observability_solution/infra/public/alerting @elastic/obs-ux-infra_services-team @@ -1181,6 +1188,7 @@ x-pack/test_serverless/**/test_suites/observability/ai_assistant @elastic/obs-ai /x-pack/plugins/observability_solution/infra/server/services @elastic/obs-ux-infra_services-team /x-pack/plugins/observability_solution/infra/server/usage @elastic/obs-ux-infra_services-team /x-pack/plugins/observability_solution/infra/server/utils @elastic/obs-ux-infra_services-team +/x-pack/test/api_integration/deployment_agnostic/apis/observability/infra @elastic/obs-ux-logs-team ## Logs UI code exceptions -> @elastic/obs-ux-logs-team /x-pack/test_serverless/functional/page_objects/svl_oblt_onboarding_stream_log_file.ts @elastic/obs-ux-logs-team @@ -1234,6 +1242,8 @@ x-pack/test_serverless/**/test_suites/observability/ai_assistant @elastic/obs-ai /x-pack/test/accessibility/apps/group3/stack_monitoring.ts @elastic/stack-monitoring # Fleet +/x-pack/test_serverless/api_integration/test_suites/security/fleet @elastic/fleet +/x-pack/test/fleet_packages @elastic/fleet /x-pack/test/fleet_api_integration @elastic/fleet /x-pack/test/fleet_cypress @elastic/fleet /x-pack/test/fleet_functional @elastic/fleet @@ -1286,6 +1296,7 @@ x-pack/test_serverless/**/test_suites/observability/ai_assistant @elastic/obs-ai ### END Observability Plugins # Presentation +/x-pack/test/disable_ems @elastic/kibana-presentation /x-pack/test/functional/apps/dashboard @elastic/kibana-presentation /x-pack/test/accessibility/apps/group3/maps.ts @elastic/kibana-presentation /x-pack/test/accessibility/apps/group1/dashboard_panel_options.ts @elastic/kibana-presentation @@ -1320,6 +1331,7 @@ x-pack/test_serverless/**/test_suites/observability/ai_assistant @elastic/obs-ai /x-pack/test/api_integration/services/ml.ts @elastic/ml-ui # Additional plugins and packages maintained by the ML team. +/test/examples/response_stream @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 @@ -1409,6 +1421,10 @@ x-pack/test/api_integration/deployment_agnostic/services/ @elastic/appex-qa x-pack/test/**/deployment_agnostic/ @elastic/appex-qa #temporarily to monitor tests migration # Core +/test/plugin_functional/plugins/rendering_plugin @elastic/kibana-core +/test/plugin_functional/plugins/session_notifications @elastic/kibana-core +/x-pack/test/cloud_integration/plugins/saml_provider @elastic/kibana-core +/x-pack/test/functional_embedded/plugins/iframe_embedded @elastic/kibana-core /x-pack/test/functional/apps/saved_objects_management @elastic/kibana-core /x-pack/test/usage_collection @elastic/kibana-core /x-pack/test/licensing_plugin @elastic/kibana-core @@ -1446,7 +1462,6 @@ x-pack/test/**/deployment_agnostic/ @elastic/appex-qa #temporarily to monitor te # AppEx Platform Services Security //x-pack/test_serverless/api_integration/test_suites/common/security_response_headers.ts @elastic/kibana-security /x-pack/test/api_integration/apis/es @elastic/kibana-security - /x-pack/test/api_integration/apis/features @elastic/kibana-security # Kibana Telemetry @@ -1499,12 +1514,12 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib #CC# /x-pack/plugins/security/ @elastic/kibana-security # Response Ops team +/x-pack/test/examples/triggers_actions_ui_examples @elastic/response-ops +/x-pack/test/functional_with_es_ssl/plugins/cases @elastic/response-ops /x-pack/test/screenshot_creation/apps/response_ops_docs @elastic/response-ops /x-pack/test/rule_registry @elastic/response-ops @elastic/obs-ux-management-team /x-pack/test/accessibility/apps/group3/rules_connectors.ts @elastic/response-ops /x-pack/test/functional/es_archives/cases/default @elastic/response-ops -/x-pack/test_serverless/api_integration/test_suites/observability/config.ts @elastic/response-ops -/x-pack/test_serverless/api_integration/test_suites/observability/index.ts @elastic/response-ops /x-pack/test_serverless/functional/page_objects/svl_triggers_actions_ui_page.ts @elastic/response-ops /x-pack/test_serverless/functional/page_objects/svl_rule_details_ui_page.ts @elastic/response-ops /x-pack/test_serverless/functional/page_objects/svl_oblt_overview_page.ts @elastic/response-ops @@ -1559,10 +1574,17 @@ x-pack/test/api_integration/apis/management/index_management/inference_endpoints # Management Experience - Deployment Management /x-pack/test/api_integration/services/index_management.ts @elastic/kibana-management +/x-pack/test/functional/apps/license_management @elastic/kibana-management +/x-pack/test/functional/apps/painless_lab @elastic/kibana-management +/x-pack/test/functional/apps/management @elastic/kibana-management +/x-pack/test/api_integration/services/index_management.ts @elastic/kibana-management /x-pack/test/functional/services/grok_debugger.js @elastic/kibana-management +/x-pack/test/functional/apps/cross_cluster_replication @elastic/kibana-management /x-pack/test/functional/apps/grok_debugger @elastic/kibana-management /x-pack/test/functional/apps/index_lifecycle_management @elastic/kibana-management /x-pack/test/functional/apps/index_management @elastic/kibana-management +/x-pack/test/functional/apps/ingest_pipelines @elastic/kibana-management +/x-pack/test/functional/apps/upgrade_assistant @elastic/kibana-management /x-pack/test/api_integration/services/ingest_pipelines @elastic/kibana-management /x-pack/test/functional/apps/watcher @elastic/kibana-management /x-pack/test/api_integration/apis/watcher @elastic/kibana-management @@ -1605,6 +1627,7 @@ x-pack/test/api_integration/apis/management/index_management/inference_endpoints # Security Solution /x-pack/test/common/services/security_solution @elastic/security-solution /x-pack/test/api_integration/services/security_solution_*.gen.ts @elastic/security-solution +/x-pack/test_serverless/functional/test_suites/security/index.feature_flags.ts @elastic/security-solution /x-pack/test/accessibility/apps/group3/security_solution.ts @elastic/security-solution /x-pack/test_serverless/functional/test_suites/security/config.ts @elastic/security-solution @elastic/appex-qa /x-pack/test_serverless/functional/test_suites/security/config.feature_flags.ts @elastic/security-solution @@ -1877,6 +1900,7 @@ x-pack/test/security_solution_cypress/cypress/tasks/expandable_flyout @elastic/ /x-pack/plugins/security_solution/public/common/components/threat_match @elastic/security-detection-engine ## Security Solution sub teams - security-defend-workflows +/x-pack/test/defend_workflows_cypress @elastic/security-defend-workflows /x-pack/test/api_integration/apis/osquery @elastic/security-defend-workflows /x-pack/plugins/security_solution/public/management/ @elastic/security-defend-workflows /x-pack/plugins/security_solution/public/common/lib/endpoint/ @elastic/security-defend-workflows @@ -1979,6 +2003,7 @@ x-pack/plugins/security_solution/server/lib/security_integrations @elastic/secur # Logstash /x-pack/test/api_integration/apis/logstash @elastic/logstash +/x-pack/test/functional/apps/logstash @elastic/logstash #CC# /x-pack/plugins/logstash/ @elastic/logstash # EUI team @@ -1997,11 +2022,19 @@ x-pack/test/profiling_api_integration @elastic/obs-ux-infra_services-team x-pack/plugins/observability_solution/observability_shared/public/components/profiling @elastic/obs-ux-infra_services-team # Shared UX +/test/api_integration/apis/guided_onboarding @elastic/appex-sharedux +/test/plugin_functional/test_suites/shared_ux @elastic/appex-sharedux +/x-pack/test/functional/apps/advanced_settings @elastic/appex-sharedux @elastic/kibana-management +/test/examples/content_management @elastic/appex-sharedux +/test/plugin_functional/plugins/kbn_sample_panel_action @elastic/appex-sharedux +/test/functional/apps/kibana_overview @elastic/appex-sharedux +/test/plugin_functional/plugins/eui_provider_dev_warning @elastic/appex-sharedux /x-pack/test/banners_functional @elastic/appex-sharedux /x-pack/test/custom_branding @elastic/appex-sharedux -/x-pack/test/api_integration/apis/content_management @elastic/appex-sharedux -/x-pack/test/accessibility/apps/group3/tags.ts @elastic/appex-sharedux /x-pack/test/accessibility/apps/group3/snapshot_and_restore.ts @elastic/appex-sharedux +/x-pack/test/accessibility/apps/group3/tags.ts @elastic/appex-sharedux +/x-pack/test/api_integration/apis/content_management @elastic/appex-sharedux +/x-pack/test/functional_solution_sidenav @elastic/appex-sharedux /x-pack/test_serverless/functional/test_suites/common/spaces/spaces_selection.ts @elastic/appex-sharedux /x-pack/test_serverless/functional/test_suites/common/spaces/index.ts @elastic/appex-sharedux packages/react @elastic/appex-sharedux diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 2d75da66a0b43..0288504afa834 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2024-10-29 +date: 2024-11-05 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 da5a3d8bcdf78..738bba2f61023 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/ai_assistant_management_selection.mdx b/api_docs/ai_assistant_management_selection.mdx index 5ee081f33521b..1d4a76169f5e6 100644 --- a/api_docs/ai_assistant_management_selection.mdx +++ b/api_docs/ai_assistant_management_selection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiAssistantManagementSelection title: "aiAssistantManagementSelection" image: https://source.unsplash.com/400x175/?github description: API docs for the aiAssistantManagementSelection plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiAssistantManagementSelection'] --- import aiAssistantManagementSelectionObj from './ai_assistant_management_selection.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 83acd6d943974..df40cc6f13778 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2024-10-29 +date: 2024-11-05 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 55610edc6e85a..e1d6edb818426 100644 --- a/api_docs/alerting.devdocs.json +++ b/api_docs/alerting.devdocs.json @@ -3763,10 +3763,6 @@ "deprecated": true, "trackAdoption": false, "references": [ - { - "plugin": "ruleRegistry", - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts" - }, { "plugin": "ruleRegistry", "path": "x-pack/plugins/rule_registry/server/utils/create_persistence_rule_type_wrapper.ts" diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index 356d0bd184f07..1fcb9e4b2f81d 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index 8c1ae40f618fa..c6f7cdfcb87c0 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2024-10-29 +date: 2024-11-05 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 4e24b24bc1e03..32900fe685cd3 100644 --- a/api_docs/apm_data_access.mdx +++ b/api_docs/apm_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apmDataAccess title: "apmDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the apmDataAccess plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apmDataAccess'] --- import apmDataAccessObj from './apm_data_access.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index a80c596744198..ed638c66d8693 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2024-10-29 +date: 2024-11-05 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 e47d630a80bf7..284b3c211bd28 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2024-10-29 +date: 2024-11-05 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 3ba76fcb74ea6..78b7d0de9ad6e 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2024-10-29 +date: 2024-11-05 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 f52790e2615e0..a872c5d9426d7 100644 --- a/api_docs/cases.devdocs.json +++ b/api_docs/cases.devdocs.json @@ -365,7 +365,9 @@ "CustomFieldTypes", ".TOGGLE; value: boolean | null; } | { key: string; type: ", "CustomFieldTypes", - ".TEXT; value: string | null; })[] | undefined; }, \"title\" | \"description\"> | undefined; }" + ".TEXT; value: string | null; } | { key: string; type: ", + "CustomFieldTypes", + ".NUMBER; value: number | null; })[] | undefined; }, \"title\" | \"description\"> | undefined; }" ], "path": "x-pack/plugins/cases/public/client/ui/get_create_case_flyout.tsx", "deprecated": false, @@ -585,7 +587,9 @@ "CustomFieldTypes", ".TEXT; value: string | null; } | { key: string; type: ", "CustomFieldTypes", - ".TOGGLE; value: boolean | null; })[]; settings: { syncAlerts: boolean; }; status: ", + ".TOGGLE; value: boolean | null; } | { key: string; type: ", + "CustomFieldTypes", + ".NUMBER; value: number | null; })[]; settings: { syncAlerts: boolean; }; status: ", { "pluginId": "@kbn/cases-components", "scope": "common", @@ -1962,7 +1966,9 @@ "CustomFieldTypes", ".TEXT; value: string | null; } | { key: string; type: ", "CustomFieldTypes", - ".TOGGLE; value: boolean | null; })[]; settings: { syncAlerts: boolean; }; status: ", + ".TOGGLE; value: boolean | null; } | { key: string; type: ", + "CustomFieldTypes", + ".NUMBER; value: number | null; })[]; settings: { syncAlerts: boolean; }; status: ", { "pluginId": "@kbn/cases-components", "scope": "common", @@ -2164,7 +2170,9 @@ "CustomFieldTypes", ".TOGGLE; value: boolean | null; } | { key: string; type: ", "CustomFieldTypes", - ".TEXT; value: string | null; })[] | undefined; }" + ".TEXT; value: string | null; } | { key: string; type: ", + "CustomFieldTypes", + ".NUMBER; value: number | null; })[] | undefined; }" ], "path": "x-pack/plugins/cases/common/types/api/case/v1.ts", "deprecated": false, @@ -2255,7 +2263,9 @@ "CustomFieldTypes", ".TEXT; value: string | null; } | { key: string; type: ", "CustomFieldTypes", - ".TOGGLE; value: boolean | null; })[]; settings: { syncAlerts: boolean; }; status: ", + ".TOGGLE; value: boolean | null; } | { key: string; type: ", + "CustomFieldTypes", + ".NUMBER; value: number | null; })[]; settings: { syncAlerts: boolean; }; status: ", { "pluginId": "@kbn/cases-components", "scope": "common", @@ -2489,7 +2499,9 @@ "CustomFieldTypes", ".TEXT; value: string | null; } | { key: string; type: ", "CustomFieldTypes", - ".TOGGLE; value: boolean | null; })[]; settings: { syncAlerts: boolean; }; status: ", + ".TOGGLE; value: boolean | null; } | { key: string; type: ", + "CustomFieldTypes", + ".NUMBER; value: number | null; })[]; settings: { syncAlerts: boolean; }; status: ", { "pluginId": "@kbn/cases-components", "scope": "common", @@ -2676,7 +2688,9 @@ "CustomFieldTypes", ".TEXT; value: string | null; } | { key: string; type: ", "CustomFieldTypes", - ".TOGGLE; value: boolean | null; })[]; settings: { syncAlerts: boolean; }; status: ", + ".TOGGLE; value: boolean | null; } | { key: string; type: ", + "CustomFieldTypes", + ".NUMBER; value: number | null; })[]; settings: { syncAlerts: boolean; }; status: ", { "pluginId": "@kbn/cases-components", "scope": "common", @@ -2899,7 +2913,9 @@ "CustomFieldTypes", ".TEXT; value: string | null; } | { key: string; type: ", "CustomFieldTypes", - ".TOGGLE; value: boolean | null; })[]; settings: { syncAlerts: boolean; }; status: ", + ".TOGGLE; value: boolean | null; } | { key: string; type: ", + "CustomFieldTypes", + ".NUMBER; value: number | null; })[]; settings: { syncAlerts: boolean; }; status: ", { "pluginId": "@kbn/cases-components", "scope": "common", @@ -3304,7 +3320,9 @@ "CustomFieldTypes", ".TEXT; value: string | null; } | { key: string; type: ", "CustomFieldTypes", - ".TOGGLE; value: boolean | null; })[]; }; } | { type: \"comment\"; payload: { comment: { type: ", + ".TOGGLE; value: boolean | null; } | { key: string; type: ", + "CustomFieldTypes", + ".NUMBER; value: number | null; })[]; }; } | { type: \"comment\"; payload: { comment: { type: ", { "pluginId": "cases", "scope": "common", @@ -3460,7 +3478,9 @@ "CustomFieldTypes", ".TEXT; value: string | null; } | { key: string; type: ", "CustomFieldTypes", - ".TOGGLE; value: boolean | null; })[] | undefined; }; }) | { type: \"connector\"; payload: { connector: { id: string; } & (({ type: ", + ".TOGGLE; value: boolean | null; } | { key: string; type: ", + "CustomFieldTypes", + ".NUMBER; value: number | null; })[] | undefined; }; }) | { type: \"connector\"; payload: { connector: { id: string; } & (({ type: ", { "pluginId": "cases", "scope": "common", diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index f7e9f14210b02..cdcfdfd5fca76 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index 173fe94d644a5..28f8d66cfa79f 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] --- import chartsObj from './charts.devdocs.json'; diff --git a/api_docs/cloud.devdocs.json b/api_docs/cloud.devdocs.json index 08dbd1e77d8ef..354f3b99d8c05 100644 --- a/api_docs/cloud.devdocs.json +++ b/api_docs/cloud.devdocs.json @@ -893,11 +893,11 @@ "signature": [ "{ defaultSolution?: ", { - "pluginId": "cloud", - "scope": "common", - "docId": "kibCloudPluginApi", - "section": "def-common.OnBoardingDefaultSolution", - "text": "OnBoardingDefaultSolution" + "pluginId": "@kbn/core-chrome-browser", + "scope": "public", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-public.SolutionId", + "text": "SolutionId" }, " | undefined; }" ], @@ -1212,11 +1212,11 @@ "signature": [ "{ defaultSolution?: ", { - "pluginId": "cloud", - "scope": "common", - "docId": "kibCloudPluginApi", - "section": "def-common.OnBoardingDefaultSolution", - "text": "OnBoardingDefaultSolution" + "pluginId": "@kbn/core-chrome-browser", + "scope": "public", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-public.SolutionId", + "text": "SolutionId" }, " | undefined; }" ], @@ -1327,23 +1327,7 @@ "functions": [], "interfaces": [], "enums": [], - "misc": [ - { - "parentPluginId": "cloud", - "id": "def-common.OnBoardingDefaultSolution", - "type": "Type", - "tags": [], - "label": "OnBoardingDefaultSolution", - "description": [], - "signature": [ - "\"security\" | \"es\" | \"oblt\"" - ], - "path": "x-pack/plugins/cloud/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - } - ], + "misc": [], "objects": [] } } \ No newline at end of file diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index b29cf06863503..717ac3fdddee6 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 84 | 0 | 21 | 1 | +| 83 | 0 | 20 | 1 | ## Client @@ -39,8 +39,3 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core ### Start -## Common - -### Consts, variables and types - - diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index a90a7d5376e98..1a5a502a98b77 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2024-10-29 +date: 2024-11-05 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 6dc8da8c5c874..9c4e09b3cf821 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDefend'] --- import cloudDefendObj from './cloud_defend.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 5f2dfd273dc9d..71af015d61281 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2024-10-29 +date: 2024-11-05 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 b1e38f5e2bbb5..48ec716e150b8 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2024-10-29 +date: 2024-11-05 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 351923fcaeb15..42f36240b9b10 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2024-10-29 +date: 2024-11-05 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 9585d30d7fa1a..9893e995b970b 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2024-10-29 +date: 2024-11-05 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 1cce4327e1c3e..404f0f578046c 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2024-10-29 +date: 2024-11-05 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 c306076c86fe1..52e35cb9b1939 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2024-10-29 +date: 2024-11-05 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 9f1442b4d051e..6259d4fb765c1 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2024-10-29 +date: 2024-11-05 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 1e00625eecef7..5bdd8b40262a7 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; diff --git a/api_docs/data_quality.mdx b/api_docs/data_quality.mdx index ffdc05cf68768..a6beaacd49f51 100644 --- a/api_docs/data_quality.mdx +++ b/api_docs/data_quality.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataQuality title: "dataQuality" image: https://source.unsplash.com/400x175/?github description: API docs for the dataQuality plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataQuality'] --- import dataQualityObj from './data_quality.devdocs.json'; diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index d6515a64ee3c0..f04279c922cea 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 6775b65910b58..a9870f5559bb1 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; diff --git a/api_docs/data_usage.mdx b/api_docs/data_usage.mdx index 5775d930e428f..3caf106209440 100644 --- a/api_docs/data_usage.mdx +++ b/api_docs/data_usage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataUsage title: "dataUsage" image: https://source.unsplash.com/400x175/?github description: API docs for the dataUsage plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataUsage'] --- import dataUsageObj from './data_usage.devdocs.json'; diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index cdb3c711166e2..7377cfd6ab057 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2024-10-29 +date: 2024-11-05 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 c6076a60d816b..45e3c652c9a50 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2024-10-29 +date: 2024-11-05 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 59b47a782f32e..35fc66dd1ec06 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2024-10-29 +date: 2024-11-05 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 85877ea7d9650..2212e45f4731e 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2024-10-29 +date: 2024-11-05 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 e5f3ba8d5315d..5a0f884c1b74e 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/dataset_quality.mdx b/api_docs/dataset_quality.mdx index 6c9f3ad39826b..712664fb62ac1 100644 --- a/api_docs/dataset_quality.mdx +++ b/api_docs/dataset_quality.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/datasetQuality title: "datasetQuality" image: https://source.unsplash.com/400x175/?github description: API docs for the datasetQuality plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'datasetQuality'] --- import datasetQualityObj from './dataset_quality.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index 54dd102b3fc9c..bc8f6e59967f3 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -168,6 +168,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | @kbn/core-plugins-server-internal | - | | | encryptedSavedObjects | - | | | @kbn/esql-validation-autocomplete | - | +| | @kbn/monaco | - | | | reporting | - | | | @kbn/reporting-export-types-csv, reporting | - | | | @kbn/reporting-export-types-csv, reporting | - | @@ -243,6 +244,7 @@ Safe to remove. | | taskManager | | | @kbn/core-saved-objects-api-browser | | | @kbn/core-saved-objects-api-browser | +| | @kbn/esql-validation-autocomplete | | | @kbn/storybook | | | @kbn/ui-theme | | | @kbn/ui-theme | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 3e2b2b6aaf24c..90713de5f87f8 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -389,6 +389,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| +| | [hover.ts](https://github.com/elastic/kibana/tree/main/packages/kbn-monaco/src/esql/lib/hover/hover.ts#:~:text=modes) | - | | | [esql_theme.ts](https://github.com/elastic/kibana/tree/main/packages/kbn-monaco/src/esql/lib/esql_theme.ts#:~:text=darkMode), [esql_theme.ts](https://github.com/elastic/kibana/tree/main/packages/kbn-monaco/src/esql/lib/esql_theme.ts#:~:text=darkMode), [theme.ts](https://github.com/elastic/kibana/tree/main/packages/kbn-monaco/src/console/theme.ts#:~:text=darkMode), [theme.ts](https://github.com/elastic/kibana/tree/main/packages/kbn-monaco/src/console/theme.ts#:~:text=darkMode), [theme.ts](https://github.com/elastic/kibana/tree/main/packages/kbn-monaco/src/console/theme.ts#:~:text=darkMode), [theme.ts](https://github.com/elastic/kibana/tree/main/packages/kbn-monaco/src/console/theme.ts#:~:text=darkMode), [theme.ts](https://github.com/elastic/kibana/tree/main/packages/kbn-monaco/src/console/theme.ts#:~:text=darkMode), [theme.ts](https://github.com/elastic/kibana/tree/main/packages/kbn-monaco/src/console/theme.ts#:~:text=darkMode), [theme.ts](https://github.com/elastic/kibana/tree/main/packages/kbn-monaco/src/console/theme.ts#:~:text=darkMode) | - | @@ -759,7 +760,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| | | [on_save_search.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx#:~:text=SavedObjectSaveModal), [on_save_search.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx#:~:text=SavedObjectSaveModal) | 8.8.0 | -| | [get_top_nav_links.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx#:~:text=shareableUrlForSavedObject) | - | +| | [get_share.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_share.tsx#:~:text=shareableUrlForSavedObject) | - | | | [plugin.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/plugin.tsx#:~:text=executeTriggerActions) | - | | | [discover_state.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/state_management/discover_state.test.ts#:~:text=savedObjects) | - | | | [discover_state.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/state_management/discover_state.test.ts#:~:text=resolve) | - | @@ -1238,7 +1239,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [create_lifecycle_executor.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts#:~:text=alertFactory), [create_persistence_rule_type_wrapper.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/rule_registry/server/utils/create_persistence_rule_type_wrapper.ts#:~:text=alertFactory), [create_persistence_rule_type_wrapper.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/rule_registry/server/utils/create_persistence_rule_type_wrapper.ts#:~:text=alertFactory), [rule_executor.test_helpers.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/rule_registry/server/utils/rule_executor.test_helpers.ts#:~:text=alertFactory) | - | +| | [create_persistence_rule_type_wrapper.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/rule_registry/server/utils/create_persistence_rule_type_wrapper.ts#:~:text=alertFactory), [create_persistence_rule_type_wrapper.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/rule_registry/server/utils/create_persistence_rule_type_wrapper.ts#:~:text=alertFactory), [rule_executor.test_helpers.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/rule_registry/server/utils/rule_executor.test_helpers.ts#:~:text=alertFactory) | - | | | [alerts_client_factory.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.ts#:~:text=audit), [search_strategy.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.ts#:~:text=audit) | - | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 3236c8c102677..f600568eb9804 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 8b6b6275571ed..adac6f7bd8458 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2024-10-29 +date: 2024-11-05 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 daefbbeb4072f..2eb8c1f28f089 100644 --- a/api_docs/discover.devdocs.json +++ b/api_docs/discover.devdocs.json @@ -1807,24 +1807,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "discover", - "id": "def-public.TopNavCustomization.getMenuItems", - "type": "Function", - "tags": [], - "label": "getMenuItems", - "description": [], - "signature": [ - "(() => ", - "TopNavMenuItem", - "[]) | undefined" - ], - "path": "src/plugins/discover/public/customizations/customization_types/top_nav_customization.ts", - "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] - }, { "parentPluginId": "discover", "id": "def-public.TopNavCustomization.defaultBadges", @@ -1839,24 +1821,6 @@ "path": "src/plugins/discover/public/customizations/customization_types/top_nav_customization.ts", "deprecated": false, "trackAdoption": false - }, - { - "parentPluginId": "discover", - "id": "def-public.TopNavCustomization.getBadges", - "type": "Function", - "tags": [], - "label": "getBadges", - "description": [], - "signature": [ - "(() => ", - "TopNavBadge", - "[]) | undefined" - ], - "path": "src/plugins/discover/public/customizations/customization_types/top_nav_customization.ts", - "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] } ], "initialIsOpen": false diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index 6ae711bfe0f69..798d6cf8ef684 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2024-10-29 +date: 2024-11-05 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 | |-------------------|-----------|------------------------|-----------------| -| 150 | 0 | 102 | 26 | +| 148 | 0 | 100 | 24 | ## Client diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index c165805a3c858..9ec1e05328da4 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/discover_shared.mdx b/api_docs/discover_shared.mdx index 520134261ee26..d475fa791b9bb 100644 --- a/api_docs/discover_shared.mdx +++ b/api_docs/discover_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverShared title: "discoverShared" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverShared plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverShared'] --- import discoverSharedObj from './discover_shared.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index 112349452636d..07f21c506e931 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2024-10-29 +date: 2024-11-05 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 61acb1f436b44..7f971b1e2be08 100644 --- a/api_docs/elastic_assistant.mdx +++ b/api_docs/elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/elasticAssistant title: "elasticAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the elasticAssistant plugin -date: 2024-10-29 +date: 2024-11-05 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 76121c2efa099..479ec38980c53 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2024-10-29 +date: 2024-11-05 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 259a8fe021e37..6765f53629533 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2024-10-29 +date: 2024-11-05 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 a2bf51291f10e..c3233c90f6000 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2024-10-29 +date: 2024-11-05 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 e91b0600d1171..ae8b5bbb23dac 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/entities_data_access.mdx b/api_docs/entities_data_access.mdx index 4f6b523ffad01..435ad9cb85373 100644 --- a/api_docs/entities_data_access.mdx +++ b/api_docs/entities_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/entitiesDataAccess title: "entitiesDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the entitiesDataAccess plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'entitiesDataAccess'] --- import entitiesDataAccessObj from './entities_data_access.devdocs.json'; diff --git a/api_docs/entity_manager.mdx b/api_docs/entity_manager.mdx index 9f7c4cd124b03..3f3459c6a4b2a 100644 --- a/api_docs/entity_manager.mdx +++ b/api_docs/entity_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/entityManager title: "entityManager" image: https://source.unsplash.com/400x175/?github description: API docs for the entityManager plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'entityManager'] --- import entityManagerObj from './entity_manager.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index 6bcd7500e810c..550f0a626fcd7 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/esql.mdx b/api_docs/esql.mdx index 0f03fbc026bd7..f3e517ab2de0c 100644 --- a/api_docs/esql.mdx +++ b/api_docs/esql.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esql title: "esql" image: https://source.unsplash.com/400x175/?github description: API docs for the esql plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esql'] --- import esqlObj from './esql.devdocs.json'; diff --git a/api_docs/esql_data_grid.mdx b/api_docs/esql_data_grid.mdx index 2a2fa6cf796e2..758b11bd9f2cf 100644 --- a/api_docs/esql_data_grid.mdx +++ b/api_docs/esql_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esqlDataGrid title: "esqlDataGrid" image: https://source.unsplash.com/400x175/?github description: API docs for the esqlDataGrid plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esqlDataGrid'] --- import esqlDataGridObj from './esql_data_grid.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 00d17f06c89df..682ef70f5f165 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2024-10-29 +date: 2024-11-05 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 9d9d9501d00f5..c01a3923d3a61 100644 --- a/api_docs/event_annotation_listing.mdx +++ b/api_docs/event_annotation_listing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotationListing title: "eventAnnotationListing" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotationListing plugin -date: 2024-10-29 +date: 2024-11-05 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 e4f1543fae610..c584de3a85c59 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/exploratory_view.mdx b/api_docs/exploratory_view.mdx index c38b67efee7ed..cbaa205fca251 100644 --- a/api_docs/exploratory_view.mdx +++ b/api_docs/exploratory_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/exploratoryView title: "exploratoryView" image: https://source.unsplash.com/400x175/?github description: API docs for the exploratoryView plugin -date: 2024-10-29 +date: 2024-11-05 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 def6f9148e195..ff6680102f7e2 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2024-10-29 +date: 2024-11-05 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 606d0ebf0421b..257ec53c9591f 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2024-10-29 +date: 2024-11-05 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 4ad171f49ffdc..acfabf7226553 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2024-10-29 +date: 2024-11-05 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 f12ba59e0b4fc..61b18e1143491 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2024-10-29 +date: 2024-11-05 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 d3e2103b6b391..8f578a99acdb4 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2024-10-29 +date: 2024-11-05 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 b64355975b10f..b3d1f15ca9909 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2024-10-29 +date: 2024-11-05 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 8b4a2fd93187d..f420aeb6a37c5 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2024-10-29 +date: 2024-11-05 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 cdb28904e3cef..dcae8f1bdc294 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2024-10-29 +date: 2024-11-05 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 a18223c2d4a7e..49b813eb50c6e 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2024-10-29 +date: 2024-11-05 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 4106d3eef394f..32ca7be938bc3 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] --- import expressionRevealImageObj from './expression_reveal_image.devdocs.json'; diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index c44b855458796..c8d37852ebb5d 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2024-10-29 +date: 2024-11-05 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 6fdc46422a0c0..1fd34b4942ceb 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index bfcf01a7d24e5..a3f52f52fb078 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index 1a8b1dc201aa6..bac3fbe741486 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressions'] --- import expressionsObj from './expressions.devdocs.json'; diff --git a/api_docs/features.mdx b/api_docs/features.mdx index adbf15f38931a..1a320aca6a039 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2024-10-29 +date: 2024-11-05 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 4513fb8fd9673..a4b7c9d58da31 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/fields_metadata.mdx b/api_docs/fields_metadata.mdx index dc3e21c94edb6..89c0e522f5b2e 100644 --- a/api_docs/fields_metadata.mdx +++ b/api_docs/fields_metadata.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldsMetadata title: "fieldsMetadata" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldsMetadata plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldsMetadata'] --- import fieldsMetadataObj from './fields_metadata.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index 6d2d108c13187..7d8dc060bb16c 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2024-10-29 +date: 2024-11-05 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 7a640bc5827ed..f501a7df4a7e4 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2024-10-29 +date: 2024-11-05 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 40f0ea2bef98a..15b834f756341 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2024-10-29 +date: 2024-11-05 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 7fee0ab161b3f..a81bf95b68497 100644 --- a/api_docs/fleet.devdocs.json +++ b/api_docs/fleet.devdocs.json @@ -6516,7 +6516,7 @@ "\nReturn the status by the Agent's Policy id" ], "signature": [ - "(agentPolicyId?: string | undefined, filterKuery?: string | undefined) => Promise<{ events: number; total: number; online: number; error: number; offline: number; other: number; updating: number; inactive: number; unenrolled: number; all: number; active: number; }>" + "(agentPolicyId?: string | undefined, filterKuery?: string | undefined) => Promise<{ events: number; online: number; error: number; offline: number; other: number; updating: number; inactive: number; unenrolled: number; all: number; active: number; }>" ], "path": "x-pack/plugins/fleet/server/services/agents/agent_service.ts", "deprecated": false, @@ -22733,27 +22733,6 @@ "deprecated": false, "trackAdoption": false, "children": [ - { - "parentPluginId": "fleet", - "id": "def-common.GetAgentsResponse.list", - "type": "Array", - "tags": [], - "label": "list", - "description": [], - "signature": [ - { - "pluginId": "fleet", - "scope": "common", - "docId": "kibFleetPluginApi", - "section": "def-common.Agent", - "text": "Agent" - }, - "[] | undefined" - ], - "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "fleet", "id": "def-common.GetAgentsResponse.statusSummary", @@ -22790,7 +22769,7 @@ "label": "results", "description": [], "signature": [ - "{ events: number; total: number; online: number; error: number; offline: number; other: number; updating: number; inactive: number; unenrolled: number; all: number; active: number; }" + "{ events: number; online: number; error: number; offline: number; other: number; updating: number; inactive: number; unenrolled: number; all: number; active: number; }" ], "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", "deprecated": false, @@ -28994,19 +28973,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "fleet", - "id": "def-common.AGENT_API_ROUTES.STATUS_PATTERN_DEPRECATED", - "type": "string", - "tags": [], - "label": "STATUS_PATTERN_DEPRECATED", - "description": [ - "// deprecated since 8.0" - ], - "path": "x-pack/plugins/fleet/common/constants/routes.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "fleet", "id": "def-common.AGENT_API_ROUTES.UPGRADE_PATTERN", @@ -30328,19 +30294,6 @@ "path": "x-pack/plugins/fleet/common/constants/routes.ts", "deprecated": false, "trackAdoption": false - }, - { - "parentPluginId": "fleet", - "id": "def-common.APP_API_ROUTES.GENERATE_SERVICE_TOKEN_PATTERN_DEPRECATED", - "type": "string", - "tags": [], - "label": "GENERATE_SERVICE_TOKEN_PATTERN_DEPRECATED", - "description": [ - "// deprecated since 8.0" - ], - "path": "x-pack/plugins/fleet/common/constants/routes.ts", - "deprecated": false, - "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index ad97cc4df5f0c..98a47af890943 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2024-10-29 +date: 2024-11-05 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 | |-------------------|-----------|------------------------|-----------------| -| 1426 | 5 | 1301 | 80 | +| 1423 | 5 | 1300 | 80 | ## Client diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 2d098de8d7b76..cc46f0d874345 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2024-10-29 +date: 2024-11-05 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 9d8d18623cf42..161c4d1e50f48 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2024-10-29 +date: 2024-11-05 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 d59945c0d3c45..85b8cfafdadac 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2024-10-29 +date: 2024-11-05 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 775436e54df9b..d2f44485e7a7f 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2024-10-29 +date: 2024-11-05 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 198d5e87c8b60..c6319a4ea7429 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] --- import indexLifecycleManagementObj from './index_lifecycle_management.devdocs.json'; diff --git a/api_docs/index_management.devdocs.json b/api_docs/index_management.devdocs.json index ae73ce8dc81a9..d669189fc7e2a 100644 --- a/api_docs/index_management.devdocs.json +++ b/api_docs/index_management.devdocs.json @@ -2065,6 +2065,20 @@ "path": "x-pack/plugins/index_management/common/types/data_streams.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "indexManagement", + "id": "def-common.DataStream.indexMode", + "type": "CompoundType", + "tags": [], + "label": "indexMode", + "description": [], + "signature": [ + "\"standard\" | \"time_series\" | \"logsdb\"" + ], + "path": "x-pack/plugins/index_management/common/types/data_streams.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -2262,6 +2276,20 @@ "path": "x-pack/plugins/index_management/common/types/data_streams.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "indexManagement", + "id": "def-common.EnhancedDataStreamFromEs.index_mode", + "type": "CompoundType", + "tags": [], + "label": "index_mode", + "description": [], + "signature": [ + "string | null | undefined" + ], + "path": "x-pack/plugins/index_management/common/types/data_streams.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -2615,145 +2643,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "indexManagement", - "id": "def-common.IndexModule", - "type": "Interface", - "tags": [], - "label": "IndexModule", - "description": [], - "path": "x-pack/plugins/index_management/common/types/indices.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "indexManagement", - "id": "def-common.IndexModule.number_of_shards", - "type": "CompoundType", - "tags": [], - "label": "number_of_shards", - "description": [], - "signature": [ - "string | number" - ], - "path": "x-pack/plugins/index_management/common/types/indices.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "indexManagement", - "id": "def-common.IndexModule.codec", - "type": "string", - "tags": [], - "label": "codec", - "description": [], - "path": "x-pack/plugins/index_management/common/types/indices.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "indexManagement", - "id": "def-common.IndexModule.routing_partition_size", - "type": "number", - "tags": [], - "label": "routing_partition_size", - "description": [], - "path": "x-pack/plugins/index_management/common/types/indices.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "indexManagement", - "id": "def-common.IndexModule.refresh_interval", - "type": "string", - "tags": [], - "label": "refresh_interval", - "description": [], - "path": "x-pack/plugins/index_management/common/types/indices.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "indexManagement", - "id": "def-common.IndexModule.load_fixed_bitset_filters_eagerly", - "type": "boolean", - "tags": [], - "label": "load_fixed_bitset_filters_eagerly", - "description": [], - "path": "x-pack/plugins/index_management/common/types/indices.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "indexManagement", - "id": "def-common.IndexModule.shard", - "type": "Object", - "tags": [], - "label": "shard", - "description": [], - "signature": [ - "{ check_on_startup: boolean | \"checksum\"; }" - ], - "path": "x-pack/plugins/index_management/common/types/indices.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "indexManagement", - "id": "def-common.IndexModule.number_of_replicas", - "type": "number", - "tags": [], - "label": "number_of_replicas", - "description": [], - "path": "x-pack/plugins/index_management/common/types/indices.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "indexManagement", - "id": "def-common.IndexModule.auto_expand_replicas", - "type": "CompoundType", - "tags": [], - "label": "auto_expand_replicas", - "description": [], - "signature": [ - "string | false" - ], - "path": "x-pack/plugins/index_management/common/types/indices.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "indexManagement", - "id": "def-common.IndexModule.lifecycle", - "type": "Object", - "tags": [], - "label": "lifecycle", - "description": [], - "signature": [ - "LifecycleModule" - ], - "path": "x-pack/plugins/index_management/common/types/indices.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "indexManagement", - "id": "def-common.IndexModule.routing", - "type": "Object", - "tags": [], - "label": "routing", - "description": [], - "signature": [ - "{ allocation: { enable: \"none\" | \"all\" | \"primaries\" | \"new_primaries\"; }; rebalance: { enable: \"none\" | \"all\" | \"primaries\" | \"replicas\"; }; }" - ], - "path": "x-pack/plugins/index_management/common/types/indices.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "indexManagement", "id": "def-common.IndexSettings", @@ -2773,15 +2662,8 @@ "label": "index", "description": [], "signature": [ - "Partial<", - { - "pluginId": "indexManagement", - "scope": "common", - "docId": "kibIndexManagementPluginApi", - "section": "def-common.IndexModule", - "text": "IndexModule" - }, - "> | undefined" + "IndicesIndexSettingsKeys", + " | undefined" ], "path": "x-pack/plugins/index_management/common/types/indices.ts", "deprecated": false, diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index c2179eb1540dd..510ade3df9ba5 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kiban | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 250 | 0 | 245 | 1 | +| 241 | 0 | 236 | 1 | ## Client diff --git a/api_docs/inference.devdocs.json b/api_docs/inference.devdocs.json index 4fbbd82c175f9..58bc75a60d913 100644 --- a/api_docs/inference.devdocs.json +++ b/api_docs/inference.devdocs.json @@ -63,21 +63,37 @@ "description": [], "signature": [ " = ", - "ToolOptions", - ">(options: { connectorId: string; system?: string | undefined; messages: ", { - "pluginId": "inference", + "pluginId": "@kbn/inference-common", "scope": "common", - "docId": "kibInferencePluginApi", - "section": "def-common.Message", - "text": "Message" + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ToolOptions", + "text": "ToolOptions" + }, + ">(options: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompleteOptions", + "text": "ChatCompleteOptions" + }, + ") => ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionResponse", + "text": "ChatCompletionResponse" }, - "[]; functionCalling?: ", - "FunctionCallingMode", - " | undefined; } & TToolOptions) => ", - "ChatCompletionResponse", "" ], "path": "x-pack/plugins/inference/public/types.ts", @@ -95,17 +111,23 @@ "signature": [ "{ connectorId: string; system?: string | undefined; messages: ", { - "pluginId": "inference", + "pluginId": "@kbn/inference-common", "scope": "common", - "docId": "kibInferencePluginApi", + "docId": "kibKbnInferenceCommonPluginApi", "section": "def-common.Message", "text": "Message" }, "[]; functionCalling?: ", - "FunctionCallingMode", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.FunctionCallingMode", + "text": "FunctionCallingMode" + }, " | undefined; } & TToolOptions" ], - "path": "x-pack/plugins/inference/common/chat_complete/index.ts", + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/api.ts", "deprecated": false, "trackAdoption": false } @@ -121,21 +143,29 @@ "signature": [ "(id: TId, options: { connectorId: string; system?: string | undefined; input: string; schema?: TOutputSchema | undefined; previousMessages?: ", { - "pluginId": "inference", + "pluginId": "@kbn/inference-common", "scope": "common", - "docId": "kibInferencePluginApi", + "docId": "kibKbnInferenceCommonPluginApi", "section": "def-common.Message", "text": "Message" }, "[] | undefined; functionCalling?: ", - "FunctionCallingMode", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.FunctionCallingMode", + "text": "FunctionCallingMode" + }, " | undefined; }) => ", - "Observable", - "<", - "OutputEvent", - " : undefined>>" + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.OutputResponse", + "text": "OutputResponse" + }, + "" ], "path": "x-pack/plugins/inference/public/types.ts", "deprecated": false, @@ -152,7 +182,7 @@ "signature": [ "TId" ], - "path": "x-pack/plugins/inference/common/output/index.ts", + "path": "x-pack/packages/ai-infra/inference-common/src/output/api.ts", "deprecated": false, "trackAdoption": false }, @@ -166,17 +196,23 @@ "signature": [ "{ connectorId: string; system?: string | undefined; input: string; schema?: TOutputSchema | undefined; previousMessages?: ", { - "pluginId": "inference", + "pluginId": "@kbn/inference-common", "scope": "common", - "docId": "kibInferencePluginApi", + "docId": "kibKbnInferenceCommonPluginApi", "section": "def-common.Message", "text": "Message" }, "[] | undefined; functionCalling?: ", - "FunctionCallingMode", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.FunctionCallingMode", + "text": "FunctionCallingMode" + }, " | undefined; }" ], - "path": "x-pack/plugins/inference/common/output/index.ts", + "path": "x-pack/packages/ai-infra/inference-common/src/output/api.ts", "deprecated": false, "trackAdoption": false } @@ -247,69 +283,6 @@ ], "returnComment": [], "initialIsOpen": false - }, - { - "parentPluginId": "inference", - "id": "def-server.withoutChunkEvents", - "type": "Function", - "tags": [], - "label": "withoutChunkEvents", - "description": [], - "signature": [ - "() => ", - "OperatorFunction", - ">" - ], - "path": "x-pack/plugins/inference/common/chat_complete/without_chunk_events.ts", - "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "inference", - "id": "def-server.withoutOutputUpdateEvents", - "type": "Function", - "tags": [], - "label": "withoutOutputUpdateEvents", - "description": [], - "signature": [ - "() => ", - "OperatorFunction", - ">" - ], - "path": "x-pack/plugins/inference/common/output/without_output_update_events.ts", - "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "inference", - "id": "def-server.withoutTokenCountEvents", - "type": "Function", - "tags": [], - "label": "withoutTokenCountEvents", - "description": [], - "signature": [ - "() => ", - "OperatorFunction", - ">" - ], - "path": "x-pack/plugins/inference/common/chat_complete/without_token_count_events.ts", - "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [], - "initialIsOpen": false } ], "interfaces": [ @@ -335,21 +308,37 @@ ], "signature": [ " = ", - "ToolOptions", - ">(options: { connectorId: string; system?: string | undefined; messages: ", { - "pluginId": "inference", + "pluginId": "@kbn/inference-common", "scope": "common", - "docId": "kibInferencePluginApi", - "section": "def-common.Message", - "text": "Message" + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ToolOptions", + "text": "ToolOptions" + }, + ">(options: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompleteOptions", + "text": "ChatCompleteOptions" + }, + ") => ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionResponse", + "text": "ChatCompletionResponse" }, - "[]; functionCalling?: ", - "FunctionCallingMode", - " | undefined; } & TToolOptions) => ", - "ChatCompletionResponse", "" ], "path": "x-pack/plugins/inference/server/types.ts", @@ -367,17 +356,23 @@ "signature": [ "{ connectorId: string; system?: string | undefined; messages: ", { - "pluginId": "inference", + "pluginId": "@kbn/inference-common", "scope": "common", - "docId": "kibInferencePluginApi", + "docId": "kibKbnInferenceCommonPluginApi", "section": "def-common.Message", "text": "Message" }, "[]; functionCalling?: ", - "FunctionCallingMode", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.FunctionCallingMode", + "text": "FunctionCallingMode" + }, " | undefined; } & TToolOptions" ], - "path": "x-pack/plugins/inference/common/chat_complete/index.ts", + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/api.ts", "deprecated": false, "trackAdoption": false } @@ -395,21 +390,29 @@ "signature": [ "(id: TId, options: { connectorId: string; system?: string | undefined; input: string; schema?: TOutputSchema | undefined; previousMessages?: ", { - "pluginId": "inference", + "pluginId": "@kbn/inference-common", "scope": "common", - "docId": "kibInferencePluginApi", + "docId": "kibKbnInferenceCommonPluginApi", "section": "def-common.Message", "text": "Message" }, "[] | undefined; functionCalling?: ", - "FunctionCallingMode", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.FunctionCallingMode", + "text": "FunctionCallingMode" + }, " | undefined; }) => ", - "Observable", - "<", - "OutputEvent", - " : undefined>>" + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.OutputResponse", + "text": "OutputResponse" + }, + "" ], "path": "x-pack/plugins/inference/server/types.ts", "deprecated": false, @@ -426,7 +429,7 @@ "signature": [ "TId" ], - "path": "x-pack/plugins/inference/common/output/index.ts", + "path": "x-pack/packages/ai-infra/inference-common/src/output/api.ts", "deprecated": false, "trackAdoption": false }, @@ -440,17 +443,23 @@ "signature": [ "{ connectorId: string; system?: string | undefined; input: string; schema?: TOutputSchema | undefined; previousMessages?: ", { - "pluginId": "inference", + "pluginId": "@kbn/inference-common", "scope": "common", - "docId": "kibInferencePluginApi", + "docId": "kibKbnInferenceCommonPluginApi", "section": "def-common.Message", "text": "Message" }, "[] | undefined; functionCalling?: ", - "FunctionCallingMode", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.FunctionCallingMode", + "text": "FunctionCallingMode" + }, " | undefined; }" ], - "path": "x-pack/plugins/inference/common/output/index.ts", + "path": "x-pack/packages/ai-infra/inference-common/src/output/api.ts", "deprecated": false, "trackAdoption": false } @@ -610,192 +619,50 @@ }, { "parentPluginId": "inference", - "id": "def-common.generateFakeToolCallId", + "id": "def-common.createOutputApi", "type": "Function", "tags": [], - "label": "generateFakeToolCallId", + "label": "createOutputApi", "description": [], "signature": [ - "() => string" - ], - "path": "x-pack/plugins/inference/common/chat_complete/generate_fake_tool_call_id.ts", - "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "inference", - "id": "def-common.isChatCompletionChunkEvent", - "type": "Function", - "tags": [], - "label": "isChatCompletionChunkEvent", - "description": [], - "signature": [ - "(event: ", - "ChatCompletionEvent", - "<", - "ToolOptions", - ">) => boolean" - ], - "path": "x-pack/plugins/inference/common/chat_complete/is_chat_completion_chunk_event.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + "(chatCompleteApi: ", { - "parentPluginId": "inference", - "id": "def-common.isChatCompletionChunkEvent.$1", - "type": "CompoundType", - "tags": [], - "label": "event", - "description": [], - "signature": [ - "ChatCompletionEvent", - "<", - "ToolOptions", - ">" - ], - "path": "x-pack/plugins/inference/common/chat_complete/is_chat_completion_chunk_event.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "inference", - "id": "def-common.isChatCompletionEvent", - "type": "Function", - "tags": [], - "label": "isChatCompletionEvent", - "description": [], - "signature": [ - "(event: ", - "InferenceTaskEvent", - ") => boolean" - ], - "path": "x-pack/plugins/inference/common/chat_complete/is_chat_completion_event.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "inference", - "id": "def-common.isChatCompletionEvent.$1", - "type": "Object", - "tags": [], - "label": "event", - "description": [], - "signature": [ - "InferenceTaskEvent" - ], - "path": "x-pack/plugins/inference/common/chat_complete/is_chat_completion_event.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "inference", - "id": "def-common.isChatCompletionMessageEvent", - "type": "Function", - "tags": [], - "label": "isChatCompletionMessageEvent", - "description": [], - "signature": [ - "(event: ", - "ChatCompletionEvent", - ") => boolean" - ], - "path": "x-pack/plugins/inference/common/chat_complete/is_chat_completion_message_event.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "inference", - "id": "def-common.isChatCompletionMessageEvent.$1", - "type": "CompoundType", - "tags": [], - "label": "event", - "description": [], - "signature": [ - "ChatCompletionEvent", - "" - ], - "path": "x-pack/plugins/inference/common/chat_complete/is_chat_completion_message_event.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "inference", - "id": "def-common.isOutputCompleteEvent", - "type": "Function", - "tags": [], - "label": "isOutputCompleteEvent", - "description": [], - "signature": [ - "(event: TOutputEvent) => boolean" - ], - "path": "x-pack/plugins/inference/common/output/is_output_complete_event.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompleteAPI", + "text": "ChatCompleteAPI" + }, + ") => ", { - "parentPluginId": "inference", - "id": "def-common.isOutputCompleteEvent.$1", - "type": "Uncategorized", - "tags": [], - "label": "event", - "description": [], - "signature": [ - "TOutputEvent" - ], - "path": "x-pack/plugins/inference/common/output/is_output_complete_event.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.OutputAPI", + "text": "OutputAPI" } ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "inference", - "id": "def-common.isOutputEvent", - "type": "Function", - "tags": [], - "label": "isOutputEvent", - "description": [], - "signature": [ - "(event: ", - "InferenceTaskEvent", - ") => boolean" - ], - "path": "x-pack/plugins/inference/common/output/is_output_event.ts", + "path": "x-pack/plugins/inference/common/create_output_api.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "inference", - "id": "def-common.isOutputEvent.$1", - "type": "Object", + "id": "def-common.createOutputApi.$1", + "type": "Function", "tags": [], - "label": "event", + "label": "chatCompleteApi", "description": [], "signature": [ - "InferenceTaskEvent" + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompleteAPI", + "text": "ChatCompleteAPI" + } ], - "path": "x-pack/plugins/inference/common/output/is_output_event.ts", + "path": "x-pack/plugins/inference/common/create_output_api.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -806,37 +673,18 @@ }, { "parentPluginId": "inference", - "id": "def-common.isOutputUpdateEvent", + "id": "def-common.generateFakeToolCallId", "type": "Function", "tags": [], - "label": "isOutputUpdateEvent", + "label": "generateFakeToolCallId", "description": [], "signature": [ - "(event: ", - "OutputEvent", - ") => boolean" + "() => string" ], - "path": "x-pack/plugins/inference/common/output/is_output_update_event.ts", + "path": "x-pack/plugins/inference/common/utils/generate_fake_tool_call_id.ts", "deprecated": false, "trackAdoption": false, - "children": [ - { - "parentPluginId": "inference", - "id": "def-common.isOutputUpdateEvent.$1", - "type": "CompoundType", - "tags": [], - "label": "event", - "description": [], - "signature": [ - "OutputEvent", - "" - ], - "path": "x-pack/plugins/inference/common/output/is_output_update_event.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], + "children": [], "returnComment": [], "initialIsOpen": false }, @@ -874,142 +722,74 @@ "initialIsOpen": false } ], - "interfaces": [], - "enums": [ + "interfaces": [ { "parentPluginId": "inference", - "id": "def-common.MessageRole", - "type": "Enum", + "id": "def-common.GetConnectorsResponseBody", + "type": "Interface", "tags": [], - "label": "MessageRole", + "label": "GetConnectorsResponseBody", "description": [], - "path": "x-pack/plugins/inference/common/chat_complete/index.ts", + "path": "x-pack/plugins/inference/common/http_apis.ts", "deprecated": false, "trackAdoption": false, + "children": [ + { + "parentPluginId": "inference", + "id": "def-common.GetConnectorsResponseBody.connectors", + "type": "Array", + "tags": [], + "label": "connectors", + "description": [], + "signature": [ + "InferenceConnector", + "[]" + ], + "path": "x-pack/plugins/inference/common/http_apis.ts", + "deprecated": false, + "trackAdoption": false + } + ], "initialIsOpen": false } ], + "enums": [], "misc": [ { "parentPluginId": "inference", - "id": "def-common.AssistantMessage", - "type": "Type", - "tags": [], - "label": "AssistantMessage", - "description": [], - "signature": [ - "MessageBase<", - { - "pluginId": "inference", - "scope": "common", - "docId": "kibInferencePluginApi", - "section": "def-common.MessageRole", - "text": "MessageRole" - }, - ".Assistant> & { content: string | null; toolCalls?: ", - "ToolCall", - " | undefined>[] | undefined; }" - ], - "path": "x-pack/plugins/inference/common/chat_complete/index.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "inference", - "id": "def-common.Message", + "id": "def-common.ChatCompleteRequestBody", "type": "Type", "tags": [], - "label": "Message", + "label": "ChatCompleteRequestBody", "description": [], "signature": [ + "{ connectorId: string; stream?: boolean | undefined; system?: string | undefined; messages: ", { - "pluginId": "inference", - "scope": "common", - "docId": "kibInferencePluginApi", - "section": "def-common.UserMessage", - "text": "UserMessage" - }, - " | ", - { - "pluginId": "inference", + "pluginId": "@kbn/inference-common", "scope": "common", - "docId": "kibInferencePluginApi", - "section": "def-common.AssistantMessage", - "text": "AssistantMessage" + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.Message", + "text": "Message" }, - " | ", + "[]; functionCalling?: ", { - "pluginId": "inference", + "pluginId": "@kbn/inference-common", "scope": "common", - "docId": "kibInferencePluginApi", - "section": "def-common.ToolMessage", - "text": "ToolMessage" + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.FunctionCallingMode", + "text": "FunctionCallingMode" }, - "" - ], - "path": "x-pack/plugins/inference/common/chat_complete/index.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "inference", - "id": "def-common.ToolMessage", - "type": "Type", - "tags": [], - "label": "ToolMessage", - "description": [], - "signature": [ - "MessageBase<", - { - "pluginId": "inference", - "scope": "common", - "docId": "kibInferencePluginApi", - "section": "def-common.MessageRole", - "text": "MessageRole" - }, - ".Tool> & { toolCallId: string; response: TToolResponse; }" - ], - "path": "x-pack/plugins/inference/common/chat_complete/index.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "inference", - "id": "def-common.ToolSchema", - "type": "Type", - "tags": [], - "label": "ToolSchema", - "description": [], - "signature": [ - "ToolSchemaTypeObject" - ], - "path": "x-pack/plugins/inference/common/chat_complete/tool_schema.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "inference", - "id": "def-common.UserMessage", - "type": "Type", - "tags": [], - "label": "UserMessage", - "description": [], - "signature": [ - "MessageBase<", + " | undefined; } & ", { - "pluginId": "inference", + "pluginId": "@kbn/inference-common", "scope": "common", - "docId": "kibInferencePluginApi", - "section": "def-common.MessageRole", - "text": "MessageRole" + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ToolOptions", + "text": "ToolOptions" }, - ".User> & { content: string; }" + "" ], - "path": "x-pack/plugins/inference/common/chat_complete/index.ts", + "path": "x-pack/plugins/inference/common/http_apis.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false diff --git a/api_docs/inference.mdx b/api_docs/inference.mdx index 201b3cf8db86f..6ef14184c25cb 100644 --- a/api_docs/inference.mdx +++ b/api_docs/inference.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inference title: "inference" image: https://source.unsplash.com/400x175/?github description: API docs for the inference plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inference'] --- import inferenceObj from './inference.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-ai-infra](https://github.com/orgs/elastic/teams/appex-ai | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 49 | 0 | 44 | 15 | +| 33 | 0 | 28 | 4 | ## Client @@ -53,8 +53,8 @@ Contact [@elastic/appex-ai-infra](https://github.com/orgs/elastic/teams/appex-ai ### Functions -### Enums - +### Interfaces + ### Consts, variables and types diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index 937fc1514a4f9..4250e845d0609 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/ingest_pipelines.mdx b/api_docs/ingest_pipelines.mdx index 880ed3e30ab78..41be784933754 100644 --- a/api_docs/ingest_pipelines.mdx +++ b/api_docs/ingest_pipelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ingestPipelines title: "ingestPipelines" image: https://source.unsplash.com/400x175/?github description: API docs for the ingestPipelines plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ingestPipelines'] --- import ingestPipelinesObj from './ingest_pipelines.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index af217ac06b393..d31c0a34e4e2d 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/integration_assistant.mdx b/api_docs/integration_assistant.mdx index caa28b324bda6..da75322856915 100644 --- a/api_docs/integration_assistant.mdx +++ b/api_docs/integration_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/integrationAssistant title: "integrationAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the integrationAssistant plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'integrationAssistant'] --- import integrationAssistantObj from './integration_assistant.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index ca11eba54cf6f..a1bc6b3c5d26b 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/inventory.devdocs.json b/api_docs/inventory.devdocs.json index 061ad0b1ed5a3..acc804d848107 100644 --- a/api_docs/inventory.devdocs.json +++ b/api_docs/inventory.devdocs.json @@ -62,6 +62,36 @@ "InventoryRouteHandlerResources", ", { hasData: boolean; }, ", "InventoryRouteCreateOptions", + ">; \"GET /internal/inventory/entities/group_by/{field}\": ", + { + "pluginId": "@kbn/server-route-repository-utils", + "scope": "common", + "docId": "kibKbnServerRouteRepositoryUtilsPluginApi", + "section": "def-common.ServerRoute", + "text": "ServerRoute" + }, + "<\"GET /internal/inventory/entities/group_by/{field}\", ", + "IntersectionC", + "<[", + "TypeC", + "<{ path: ", + "TypeC", + "<{ field: ", + "LiteralC", + "<\"entity.type\">; }>; }>, ", + "PartialC", + "<{ query: ", + "PartialC", + "<{ kuery: ", + "StringC", + "; entityTypes: ", + "Type", + "; }>; }>]>, ", + "InventoryRouteHandlerResources", + ", { groupBy: \"entity.type\"; groups: ", + "EntityGroup", + "[]; entitiesCount: number; }, ", + "InventoryRouteCreateOptions", ">; \"GET /internal/inventory/entities/types\": ", { "pluginId": "@kbn/server-route-repository-utils", diff --git a/api_docs/inventory.mdx b/api_docs/inventory.mdx index 9e6b1186f00b1..ef2ad79631e21 100644 --- a/api_docs/inventory.mdx +++ b/api_docs/inventory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inventory title: "inventory" image: https://source.unsplash.com/400x175/?github description: API docs for the inventory plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inventory'] --- import inventoryObj from './inventory.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 | |-------------------|-----------|------------------------|-----------------| -| 5 | 0 | 5 | 3 | +| 5 | 0 | 5 | 4 | ## Client diff --git a/api_docs/investigate.mdx b/api_docs/investigate.mdx index f4f2a2936cf8b..2e6459fee44e0 100644 --- a/api_docs/investigate.mdx +++ b/api_docs/investigate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/investigate title: "investigate" image: https://source.unsplash.com/400x175/?github description: API docs for the investigate plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'investigate'] --- import investigateObj from './investigate.devdocs.json'; diff --git a/api_docs/investigate_app.mdx b/api_docs/investigate_app.mdx index 93b61c0850ac6..c2f5c7b4a6d5a 100644 --- a/api_docs/investigate_app.mdx +++ b/api_docs/investigate_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/investigateApp title: "investigateApp" image: https://source.unsplash.com/400x175/?github description: API docs for the investigateApp plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'investigateApp'] --- import investigateAppObj from './investigate_app.devdocs.json'; diff --git a/api_docs/kbn_actions_types.mdx b/api_docs/kbn_actions_types.mdx index 9c99f89fbca6e..5004cf28f8ac8 100644 --- a/api_docs/kbn_actions_types.mdx +++ b/api_docs/kbn_actions_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-actions-types title: "@kbn/actions-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/actions-types plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/actions-types'] --- import kbnActionsTypesObj from './kbn_actions_types.devdocs.json'; diff --git a/api_docs/kbn_ai_assistant.devdocs.json b/api_docs/kbn_ai_assistant.devdocs.json index fd7ac303da36b..f80f569dd4300 100644 --- a/api_docs/kbn_ai_assistant.devdocs.json +++ b/api_docs/kbn_ai_assistant.devdocs.json @@ -349,7 +349,7 @@ "label": "ChatFlyout", "description": [], "signature": [ - "({\n initialTitle,\n initialMessages,\n initialFlyoutPositionMode,\n isOpen,\n onClose,\n navigateToConversation,\n}: { initialTitle: string; initialMessages: ", + "({\n initialTitle,\n initialMessages,\n initialFlyoutPositionMode,\n isOpen,\n onClose,\n navigateToConversation,\n hideConversationList,\n}: { initialTitle: string; initialMessages: ", { "pluginId": "observabilityAIAssistant", "scope": "common", @@ -365,7 +365,7 @@ "section": "def-public.FlyoutPositionMode", "text": "FlyoutPositionMode" }, - " | undefined; isOpen: boolean; onClose: () => void; navigateToConversation?: ((conversationId?: string | undefined) => void) | undefined; }) => React.JSX.Element | null" + " | undefined; isOpen: boolean; onClose: () => void; navigateToConversation?: ((conversationId?: string | undefined) => void) | undefined; hideConversationList?: boolean | undefined; }) => React.JSX.Element | null" ], "path": "x-pack/packages/kbn-ai-assistant/src/chat/chat_flyout.tsx", "deprecated": false, @@ -376,7 +376,7 @@ "id": "def-public.ChatFlyout.$1", "type": "Object", "tags": [], - "label": "{\n initialTitle,\n initialMessages,\n initialFlyoutPositionMode,\n isOpen,\n onClose,\n navigateToConversation,\n}", + "label": "{\n initialTitle,\n initialMessages,\n initialFlyoutPositionMode,\n isOpen,\n onClose,\n navigateToConversation,\n hideConversationList,\n}", "description": [], "path": "x-pack/packages/kbn-ai-assistant/src/chat/chat_flyout.tsx", "deprecated": false, @@ -493,6 +493,20 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "@kbn/ai-assistant", + "id": "def-public.ChatFlyout.$1.hideConversationList", + "type": "CompoundType", + "tags": [], + "label": "hideConversationList", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/packages/kbn-ai-assistant/src/chat/chat_flyout.tsx", + "deprecated": false, + "trackAdoption": false } ] } diff --git a/api_docs/kbn_ai_assistant.mdx b/api_docs/kbn_ai_assistant.mdx index 6bd90ed7d6845..50a0f783d0e7c 100644 --- a/api_docs/kbn_ai_assistant.mdx +++ b/api_docs/kbn_ai_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ai-assistant title: "@kbn/ai-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ai-assistant plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ai-assistant'] --- import kbnAiAssistantObj from './kbn_ai_assistant.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-ki | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 63 | 0 | 63 | 1 | +| 64 | 0 | 64 | 1 | ## Client diff --git a/api_docs/kbn_ai_assistant_common.mdx b/api_docs/kbn_ai_assistant_common.mdx index fac0397b97ec3..bf0bdc7ddb516 100644 --- a/api_docs/kbn_ai_assistant_common.mdx +++ b/api_docs/kbn_ai_assistant_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ai-assistant-common title: "@kbn/ai-assistant-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ai-assistant-common plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ai-assistant-common'] --- import kbnAiAssistantCommonObj from './kbn_ai_assistant_common.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index 9765fbc78019a..69b9efacb6a40 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_pattern_analysis.mdx b/api_docs/kbn_aiops_log_pattern_analysis.mdx index c7b4c551f3b82..6bf8c7b2f32e3 100644 --- a/api_docs/kbn_aiops_log_pattern_analysis.mdx +++ b/api_docs/kbn_aiops_log_pattern_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-pattern-analysis title: "@kbn/aiops-log-pattern-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-pattern-analysis plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-pattern-analysis'] --- import kbnAiopsLogPatternAnalysisObj from './kbn_aiops_log_pattern_analysis.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_rate_analysis.mdx b/api_docs/kbn_aiops_log_rate_analysis.mdx index 5bc560ea83d8c..75f85b2e4589d 100644 --- a/api_docs/kbn_aiops_log_rate_analysis.mdx +++ b/api_docs/kbn_aiops_log_rate_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-rate-analysis title: "@kbn/aiops-log-rate-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-rate-analysis plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-rate-analysis'] --- import kbnAiopsLogRateAnalysisObj from './kbn_aiops_log_rate_analysis.devdocs.json'; diff --git a/api_docs/kbn_alerting_api_integration_helpers.mdx b/api_docs/kbn_alerting_api_integration_helpers.mdx index 2debad05e4062..ac12e11469a6a 100644 --- a/api_docs/kbn_alerting_api_integration_helpers.mdx +++ b/api_docs/kbn_alerting_api_integration_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-api-integration-helpers title: "@kbn/alerting-api-integration-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-api-integration-helpers plugin -date: 2024-10-29 +date: 2024-11-05 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_comparators.mdx b/api_docs/kbn_alerting_comparators.mdx index 3d7040c6b9c4a..7a466ff9e04a7 100644 --- a/api_docs/kbn_alerting_comparators.mdx +++ b/api_docs/kbn_alerting_comparators.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-comparators title: "@kbn/alerting-comparators" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-comparators plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-comparators'] --- import kbnAlertingComparatorsObj from './kbn_alerting_comparators.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index 2a5c6ac6877a0..7d99e21f1fa46 100644 --- a/api_docs/kbn_alerting_state_types.mdx +++ b/api_docs/kbn_alerting_state_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-state-types title: "@kbn/alerting-state-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-state-types plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-state-types'] --- import kbnAlertingStateTypesObj from './kbn_alerting_state_types.devdocs.json'; diff --git a/api_docs/kbn_alerting_types.mdx b/api_docs/kbn_alerting_types.mdx index 0b232b7be0b1d..3938fbbfa594f 100644 --- a/api_docs/kbn_alerting_types.mdx +++ b/api_docs/kbn_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-types title: "@kbn/alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-types plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-types'] --- import kbnAlertingTypesObj from './kbn_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.devdocs.json b/api_docs/kbn_alerts_as_data_utils.devdocs.json index b26349824b605..2df581305ee8b 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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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; 'container.id'?: string | undefined; 'error.grouping_key'?: string | undefined; 'error.grouping_name'?: string | undefined; 'host.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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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_status_updated_at'?: string | number | 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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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; 'container.security_context.privileged'?: boolean | 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.go_import_hash'?: string | undefined; 'dll.pe.go_imports'?: unknown; 'dll.pe.go_imports_names_entropy'?: string | number | undefined; 'dll.pe.go_imports_names_var_entropy'?: string | number | undefined; 'dll.pe.go_stripped'?: boolean | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.import_hash'?: string | undefined; 'dll.pe.imports'?: unknown[] | undefined; 'dll.pe.imports_names_entropy'?: string | number | undefined; 'dll.pe.imports_names_var_entropy'?: string | number | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dll.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.go_import_hash'?: string | undefined; 'file.elf.go_imports'?: unknown; 'file.elf.go_imports_names_entropy'?: string | number | undefined; 'file.elf.go_imports_names_var_entropy'?: string | number | undefined; 'file.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.imports_names_entropy'?: string | number | undefined; 'file.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.macho.go_import_hash'?: string | undefined; 'file.macho.go_imports'?: unknown; 'file.macho.go_imports_names_entropy'?: string | number | undefined; 'file.macho.go_imports_names_var_entropy'?: string | number | undefined; 'file.macho.go_stripped'?: boolean | undefined; 'file.macho.import_hash'?: string | undefined; 'file.macho.imports'?: unknown[] | undefined; 'file.macho.imports_names_entropy'?: string | number | undefined; 'file.macho.imports_names_var_entropy'?: string | number | undefined; 'file.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.macho.symhash'?: 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.go_import_hash'?: string | undefined; 'file.pe.go_imports'?: unknown; 'file.pe.go_imports_names_entropy'?: string | number | undefined; 'file.pe.go_imports_names_var_entropy'?: string | number | undefined; 'file.pe.go_stripped'?: boolean | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.import_hash'?: string | undefined; 'file.pe.imports'?: unknown[] | undefined; 'file.pe.imports_names_entropy'?: string | number | undefined; 'file.pe.imports_names_var_entropy'?: string | number | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.annotation'?: string[] | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.label'?: 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.go_import_hash'?: string | undefined; 'process.elf.go_imports'?: unknown; 'process.elf.go_imports_names_entropy'?: string | number | undefined; 'process.elf.go_imports_names_var_entropy'?: string | number | undefined; 'process.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.imports_names_entropy'?: string | number | undefined; 'process.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.session_leader.vpid'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.parent.vpid'?: 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.vpid'?: string | number | 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.vpid'?: string | number | 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.macho.go_import_hash'?: string | undefined; 'process.macho.go_imports'?: unknown; 'process.macho.go_imports_names_entropy'?: string | number | undefined; 'process.macho.go_imports_names_var_entropy'?: string | number | undefined; 'process.macho.go_stripped'?: boolean | undefined; 'process.macho.import_hash'?: string | undefined; 'process.macho.imports'?: unknown[] | undefined; 'process.macho.imports_names_entropy'?: string | number | undefined; 'process.macho.imports_names_var_entropy'?: string | number | undefined; 'process.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.macho.symhash'?: string | undefined; '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.go_import_hash'?: string | undefined; 'process.parent.elf.go_imports'?: unknown; 'process.parent.elf.go_imports_names_entropy'?: string | number | undefined; 'process.parent.elf.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.imports_names_entropy'?: string | number | undefined; 'process.parent.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.group_leader.vpid'?: 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.macho.go_import_hash'?: string | undefined; 'process.parent.macho.go_imports'?: unknown; 'process.parent.macho.go_imports_names_entropy'?: string | number | undefined; 'process.parent.macho.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.macho.go_stripped'?: boolean | undefined; 'process.parent.macho.import_hash'?: string | undefined; 'process.parent.macho.imports'?: unknown[] | undefined; 'process.parent.macho.imports_names_entropy'?: string | number | undefined; 'process.parent.macho.imports_names_var_entropy'?: string | number | undefined; 'process.parent.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.macho.symhash'?: string | 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.go_import_hash'?: string | undefined; 'process.parent.pe.go_imports'?: unknown; 'process.parent.pe.go_imports_names_entropy'?: string | number | undefined; 'process.parent.pe.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.pe.go_stripped'?: boolean | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.import_hash'?: string | undefined; 'process.parent.pe.imports'?: unknown[] | undefined; 'process.parent.pe.imports_names_entropy'?: string | number | undefined; 'process.parent.pe.imports_names_var_entropy'?: string | number | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.capabilities.effective'?: string[] | undefined; 'process.parent.thread.capabilities.permitted'?: 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.vpid'?: string | number | 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.go_import_hash'?: string | undefined; 'process.pe.go_imports'?: unknown; 'process.pe.go_imports_names_entropy'?: string | number | undefined; 'process.pe.go_imports_names_var_entropy'?: string | number | undefined; 'process.pe.go_stripped'?: boolean | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.import_hash'?: string | undefined; 'process.pe.imports'?: unknown[] | undefined; 'process.pe.imports_names_entropy'?: string | number | undefined; 'process.pe.imports_names_var_entropy'?: string | number | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.session_leader.vpid'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.parent.vpid'?: 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.vpid'?: string | number | 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.capabilities.effective'?: string[] | undefined; 'process.thread.capabilities.permitted'?: 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.vpid'?: string | number | 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.go_import_hash'?: string | undefined; 'threat.indicator.file.elf.go_imports'?: unknown; 'threat.indicator.file.elf.go_imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.elf.go_imports_names_var_entropy'?: string | number | undefined; 'threat.indicator.file.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.go_import_hash'?: string | undefined; 'threat.indicator.file.pe.go_imports'?: unknown; 'threat.indicator.file.pe.go_imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.pe.go_imports_names_var_entropy'?: string | number | undefined; 'threat.indicator.file.pe.go_stripped'?: boolean | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.import_hash'?: string | undefined; 'threat.indicator.file.pe.imports'?: unknown[] | undefined; 'threat.indicator.file.pe.imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.pe.imports_names_var_entropy'?: string | number | 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.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.name'?: string | 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_status_updated_at'?: string | number | 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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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; 'container.security_context.privileged'?: boolean | 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.go_import_hash'?: string | undefined; 'dll.pe.go_imports'?: unknown; 'dll.pe.go_imports_names_entropy'?: string | number | undefined; 'dll.pe.go_imports_names_var_entropy'?: string | number | undefined; 'dll.pe.go_stripped'?: boolean | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.import_hash'?: string | undefined; 'dll.pe.imports'?: unknown[] | undefined; 'dll.pe.imports_names_entropy'?: string | number | undefined; 'dll.pe.imports_names_var_entropy'?: string | number | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dll.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.go_import_hash'?: string | undefined; 'file.elf.go_imports'?: unknown; 'file.elf.go_imports_names_entropy'?: string | number | undefined; 'file.elf.go_imports_names_var_entropy'?: string | number | undefined; 'file.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.imports_names_entropy'?: string | number | undefined; 'file.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.macho.go_import_hash'?: string | undefined; 'file.macho.go_imports'?: unknown; 'file.macho.go_imports_names_entropy'?: string | number | undefined; 'file.macho.go_imports_names_var_entropy'?: string | number | undefined; 'file.macho.go_stripped'?: boolean | undefined; 'file.macho.import_hash'?: string | undefined; 'file.macho.imports'?: unknown[] | undefined; 'file.macho.imports_names_entropy'?: string | number | undefined; 'file.macho.imports_names_var_entropy'?: string | number | undefined; 'file.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.macho.symhash'?: 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.go_import_hash'?: string | undefined; 'file.pe.go_imports'?: unknown; 'file.pe.go_imports_names_entropy'?: string | number | undefined; 'file.pe.go_imports_names_var_entropy'?: string | number | undefined; 'file.pe.go_stripped'?: boolean | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.import_hash'?: string | undefined; 'file.pe.imports'?: unknown[] | undefined; 'file.pe.imports_names_entropy'?: string | number | undefined; 'file.pe.imports_names_var_entropy'?: string | number | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.annotation'?: string[] | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.label'?: 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.go_import_hash'?: string | undefined; 'process.elf.go_imports'?: unknown; 'process.elf.go_imports_names_entropy'?: string | number | undefined; 'process.elf.go_imports_names_var_entropy'?: string | number | undefined; 'process.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.imports_names_entropy'?: string | number | undefined; 'process.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.session_leader.vpid'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.parent.vpid'?: 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.vpid'?: string | number | 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.vpid'?: string | number | 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.macho.go_import_hash'?: string | undefined; 'process.macho.go_imports'?: unknown; 'process.macho.go_imports_names_entropy'?: string | number | undefined; 'process.macho.go_imports_names_var_entropy'?: string | number | undefined; 'process.macho.go_stripped'?: boolean | undefined; 'process.macho.import_hash'?: string | undefined; 'process.macho.imports'?: unknown[] | undefined; 'process.macho.imports_names_entropy'?: string | number | undefined; 'process.macho.imports_names_var_entropy'?: string | number | undefined; 'process.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.macho.symhash'?: string | undefined; '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.go_import_hash'?: string | undefined; 'process.parent.elf.go_imports'?: unknown; 'process.parent.elf.go_imports_names_entropy'?: string | number | undefined; 'process.parent.elf.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.imports_names_entropy'?: string | number | undefined; 'process.parent.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.group_leader.vpid'?: 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.macho.go_import_hash'?: string | undefined; 'process.parent.macho.go_imports'?: unknown; 'process.parent.macho.go_imports_names_entropy'?: string | number | undefined; 'process.parent.macho.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.macho.go_stripped'?: boolean | undefined; 'process.parent.macho.import_hash'?: string | undefined; 'process.parent.macho.imports'?: unknown[] | undefined; 'process.parent.macho.imports_names_entropy'?: string | number | undefined; 'process.parent.macho.imports_names_var_entropy'?: string | number | undefined; 'process.parent.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.macho.symhash'?: string | 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.go_import_hash'?: string | undefined; 'process.parent.pe.go_imports'?: unknown; 'process.parent.pe.go_imports_names_entropy'?: string | number | undefined; 'process.parent.pe.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.pe.go_stripped'?: boolean | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.import_hash'?: string | undefined; 'process.parent.pe.imports'?: unknown[] | undefined; 'process.parent.pe.imports_names_entropy'?: string | number | undefined; 'process.parent.pe.imports_names_var_entropy'?: string | number | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.capabilities.effective'?: string[] | undefined; 'process.parent.thread.capabilities.permitted'?: 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.vpid'?: string | number | 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.go_import_hash'?: string | undefined; 'process.pe.go_imports'?: unknown; 'process.pe.go_imports_names_entropy'?: string | number | undefined; 'process.pe.go_imports_names_var_entropy'?: string | number | undefined; 'process.pe.go_stripped'?: boolean | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.import_hash'?: string | undefined; 'process.pe.imports'?: unknown[] | undefined; 'process.pe.imports_names_entropy'?: string | number | undefined; 'process.pe.imports_names_var_entropy'?: string | number | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.session_leader.vpid'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.parent.vpid'?: 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.vpid'?: string | number | 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.capabilities.effective'?: string[] | undefined; 'process.thread.capabilities.permitted'?: 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.vpid'?: string | number | 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.go_import_hash'?: string | undefined; 'threat.indicator.file.elf.go_imports'?: unknown; 'threat.indicator.file.elf.go_imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.elf.go_imports_names_var_entropy'?: string | number | undefined; 'threat.indicator.file.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.go_import_hash'?: string | undefined; 'threat.indicator.file.pe.go_imports'?: unknown; 'threat.indicator.file.pe.go_imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.pe.go_imports_names_var_entropy'?: string | number | undefined; 'threat.indicator.file.pe.go_stripped'?: boolean | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.import_hash'?: string | undefined; 'threat.indicator.file.pe.imports'?: unknown[] | undefined; 'threat.indicator.file.pe.imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.pe.imports_names_var_entropy'?: string | number | 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.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.name'?: string | 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_status_updated_at'?: string | number | 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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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; 'container.security_context.privileged'?: boolean | 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.go_import_hash'?: string | undefined; 'dll.pe.go_imports'?: unknown; 'dll.pe.go_imports_names_entropy'?: string | number | undefined; 'dll.pe.go_imports_names_var_entropy'?: string | number | undefined; 'dll.pe.go_stripped'?: boolean | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.import_hash'?: string | undefined; 'dll.pe.imports'?: unknown[] | undefined; 'dll.pe.imports_names_entropy'?: string | number | undefined; 'dll.pe.imports_names_var_entropy'?: string | number | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dll.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.go_import_hash'?: string | undefined; 'file.elf.go_imports'?: unknown; 'file.elf.go_imports_names_entropy'?: string | number | undefined; 'file.elf.go_imports_names_var_entropy'?: string | number | undefined; 'file.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.imports_names_entropy'?: string | number | undefined; 'file.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.macho.go_import_hash'?: string | undefined; 'file.macho.go_imports'?: unknown; 'file.macho.go_imports_names_entropy'?: string | number | undefined; 'file.macho.go_imports_names_var_entropy'?: string | number | undefined; 'file.macho.go_stripped'?: boolean | undefined; 'file.macho.import_hash'?: string | undefined; 'file.macho.imports'?: unknown[] | undefined; 'file.macho.imports_names_entropy'?: string | number | undefined; 'file.macho.imports_names_var_entropy'?: string | number | undefined; 'file.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.macho.symhash'?: 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.go_import_hash'?: string | undefined; 'file.pe.go_imports'?: unknown; 'file.pe.go_imports_names_entropy'?: string | number | undefined; 'file.pe.go_imports_names_var_entropy'?: string | number | undefined; 'file.pe.go_stripped'?: boolean | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.import_hash'?: string | undefined; 'file.pe.imports'?: unknown[] | undefined; 'file.pe.imports_names_entropy'?: string | number | undefined; 'file.pe.imports_names_var_entropy'?: string | number | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.annotation'?: string[] | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.label'?: 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.go_import_hash'?: string | undefined; 'process.elf.go_imports'?: unknown; 'process.elf.go_imports_names_entropy'?: string | number | undefined; 'process.elf.go_imports_names_var_entropy'?: string | number | undefined; 'process.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.imports_names_entropy'?: string | number | undefined; 'process.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.session_leader.vpid'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.parent.vpid'?: 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.vpid'?: string | number | 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.vpid'?: string | number | 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.macho.go_import_hash'?: string | undefined; 'process.macho.go_imports'?: unknown; 'process.macho.go_imports_names_entropy'?: string | number | undefined; 'process.macho.go_imports_names_var_entropy'?: string | number | undefined; 'process.macho.go_stripped'?: boolean | undefined; 'process.macho.import_hash'?: string | undefined; 'process.macho.imports'?: unknown[] | undefined; 'process.macho.imports_names_entropy'?: string | number | undefined; 'process.macho.imports_names_var_entropy'?: string | number | undefined; 'process.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.macho.symhash'?: string | undefined; '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.go_import_hash'?: string | undefined; 'process.parent.elf.go_imports'?: unknown; 'process.parent.elf.go_imports_names_entropy'?: string | number | undefined; 'process.parent.elf.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.imports_names_entropy'?: string | number | undefined; 'process.parent.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.group_leader.vpid'?: 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.macho.go_import_hash'?: string | undefined; 'process.parent.macho.go_imports'?: unknown; 'process.parent.macho.go_imports_names_entropy'?: string | number | undefined; 'process.parent.macho.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.macho.go_stripped'?: boolean | undefined; 'process.parent.macho.import_hash'?: string | undefined; 'process.parent.macho.imports'?: unknown[] | undefined; 'process.parent.macho.imports_names_entropy'?: string | number | undefined; 'process.parent.macho.imports_names_var_entropy'?: string | number | undefined; 'process.parent.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.macho.symhash'?: string | 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.go_import_hash'?: string | undefined; 'process.parent.pe.go_imports'?: unknown; 'process.parent.pe.go_imports_names_entropy'?: string | number | undefined; 'process.parent.pe.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.pe.go_stripped'?: boolean | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.import_hash'?: string | undefined; 'process.parent.pe.imports'?: unknown[] | undefined; 'process.parent.pe.imports_names_entropy'?: string | number | undefined; 'process.parent.pe.imports_names_var_entropy'?: string | number | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.capabilities.effective'?: string[] | undefined; 'process.parent.thread.capabilities.permitted'?: 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.vpid'?: string | number | 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.go_import_hash'?: string | undefined; 'process.pe.go_imports'?: unknown; 'process.pe.go_imports_names_entropy'?: string | number | undefined; 'process.pe.go_imports_names_var_entropy'?: string | number | undefined; 'process.pe.go_stripped'?: boolean | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.import_hash'?: string | undefined; 'process.pe.imports'?: unknown[] | undefined; 'process.pe.imports_names_entropy'?: string | number | undefined; 'process.pe.imports_names_var_entropy'?: string | number | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.session_leader.vpid'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.parent.vpid'?: 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.vpid'?: string | number | 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.capabilities.effective'?: string[] | undefined; 'process.thread.capabilities.permitted'?: 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.vpid'?: string | number | 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.go_import_hash'?: string | undefined; 'threat.indicator.file.elf.go_imports'?: unknown; 'threat.indicator.file.elf.go_imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.elf.go_imports_names_var_entropy'?: string | number | undefined; 'threat.indicator.file.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.go_import_hash'?: string | undefined; 'threat.indicator.file.pe.go_imports'?: unknown; 'threat.indicator.file.pe.go_imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.pe.go_imports_names_var_entropy'?: string | number | undefined; 'threat.indicator.file.pe.go_stripped'?: boolean | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.import_hash'?: string | undefined; 'threat.indicator.file.pe.imports'?: unknown[] | undefined; 'threat.indicator.file.pe.imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.pe.imports_names_var_entropy'?: string | number | 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.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.name'?: string | 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_status_updated_at'?: string | number | undefined; 'kibana.alert.workflow_user'?: string | undefined; }) | ({} & { 'agent.name'?: string | undefined; 'anomaly.bucket_span.minutes'?: string | undefined; 'anomaly.start'?: string | number | undefined; configId?: string | undefined; 'error.message'?: string | undefined; 'host.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; 'location.id'?: string[] | undefined; 'location.name'?: string[] | undefined; 'monitor.id'?: string | undefined; 'monitor.name'?: string | undefined; 'monitor.state.id'?: string | undefined; 'monitor.tags'?: string[] | undefined; 'monitor.type'?: string | undefined; 'observer.geo.name'?: string[] | undefined; 'observer.name'?: string[] | undefined; 'service.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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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_status_updated_at'?: string | number | 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; 'event.original'?: string | undefined; 'host.asset.criticality'?: 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.consecutive_matches'?: string | number | 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.host.criticality_level'?: string | undefined; 'kibana.alert.intended_timestamp'?: string | 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.previous_action_group'?: 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.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: 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.severity_improving'?: boolean | 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.user.criticality_level'?: string | undefined; 'kibana.alert.workflow_assignee_ids'?: string[] | undefined; 'kibana.alert.workflow_reason'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_status_updated_at'?: string | number | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.alert.workflow_user'?: string | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; 'user.asset.criticality'?: 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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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; 'container.security_context.privileged'?: boolean | 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.go_import_hash'?: string | undefined; 'dll.pe.go_imports'?: unknown; 'dll.pe.go_imports_names_entropy'?: string | number | undefined; 'dll.pe.go_imports_names_var_entropy'?: string | number | undefined; 'dll.pe.go_stripped'?: boolean | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.import_hash'?: string | undefined; 'dll.pe.imports'?: unknown[] | undefined; 'dll.pe.imports_names_entropy'?: string | number | undefined; 'dll.pe.imports_names_var_entropy'?: string | number | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dll.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.go_import_hash'?: string | undefined; 'file.elf.go_imports'?: unknown; 'file.elf.go_imports_names_entropy'?: string | number | undefined; 'file.elf.go_imports_names_var_entropy'?: string | number | undefined; 'file.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.imports_names_entropy'?: string | number | undefined; 'file.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.macho.go_import_hash'?: string | undefined; 'file.macho.go_imports'?: unknown; 'file.macho.go_imports_names_entropy'?: string | number | undefined; 'file.macho.go_imports_names_var_entropy'?: string | number | undefined; 'file.macho.go_stripped'?: boolean | undefined; 'file.macho.import_hash'?: string | undefined; 'file.macho.imports'?: unknown[] | undefined; 'file.macho.imports_names_entropy'?: string | number | undefined; 'file.macho.imports_names_var_entropy'?: string | number | undefined; 'file.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.macho.symhash'?: 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.go_import_hash'?: string | undefined; 'file.pe.go_imports'?: unknown; 'file.pe.go_imports_names_entropy'?: string | number | undefined; 'file.pe.go_imports_names_var_entropy'?: string | number | undefined; 'file.pe.go_stripped'?: boolean | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.import_hash'?: string | undefined; 'file.pe.imports'?: unknown[] | undefined; 'file.pe.imports_names_entropy'?: string | number | undefined; 'file.pe.imports_names_var_entropy'?: string | number | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.annotation'?: string[] | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.label'?: 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.go_import_hash'?: string | undefined; 'process.elf.go_imports'?: unknown; 'process.elf.go_imports_names_entropy'?: string | number | undefined; 'process.elf.go_imports_names_var_entropy'?: string | number | undefined; 'process.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.imports_names_entropy'?: string | number | undefined; 'process.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.session_leader.vpid'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.parent.vpid'?: 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.vpid'?: string | number | 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.vpid'?: string | number | 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.macho.go_import_hash'?: string | undefined; 'process.macho.go_imports'?: unknown; 'process.macho.go_imports_names_entropy'?: string | number | undefined; 'process.macho.go_imports_names_var_entropy'?: string | number | undefined; 'process.macho.go_stripped'?: boolean | undefined; 'process.macho.import_hash'?: string | undefined; 'process.macho.imports'?: unknown[] | undefined; 'process.macho.imports_names_entropy'?: string | number | undefined; 'process.macho.imports_names_var_entropy'?: string | number | undefined; 'process.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.macho.symhash'?: string | undefined; '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.go_import_hash'?: string | undefined; 'process.parent.elf.go_imports'?: unknown; 'process.parent.elf.go_imports_names_entropy'?: string | number | undefined; 'process.parent.elf.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.imports_names_entropy'?: string | number | undefined; 'process.parent.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.group_leader.vpid'?: 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.macho.go_import_hash'?: string | undefined; 'process.parent.macho.go_imports'?: unknown; 'process.parent.macho.go_imports_names_entropy'?: string | number | undefined; 'process.parent.macho.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.macho.go_stripped'?: boolean | undefined; 'process.parent.macho.import_hash'?: string | undefined; 'process.parent.macho.imports'?: unknown[] | undefined; 'process.parent.macho.imports_names_entropy'?: string | number | undefined; 'process.parent.macho.imports_names_var_entropy'?: string | number | undefined; 'process.parent.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.macho.symhash'?: string | 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.go_import_hash'?: string | undefined; 'process.parent.pe.go_imports'?: unknown; 'process.parent.pe.go_imports_names_entropy'?: string | number | undefined; 'process.parent.pe.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.pe.go_stripped'?: boolean | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.import_hash'?: string | undefined; 'process.parent.pe.imports'?: unknown[] | undefined; 'process.parent.pe.imports_names_entropy'?: string | number | undefined; 'process.parent.pe.imports_names_var_entropy'?: string | number | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.capabilities.effective'?: string[] | undefined; 'process.parent.thread.capabilities.permitted'?: 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.vpid'?: string | number | 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.go_import_hash'?: string | undefined; 'process.pe.go_imports'?: unknown; 'process.pe.go_imports_names_entropy'?: string | number | undefined; 'process.pe.go_imports_names_var_entropy'?: string | number | undefined; 'process.pe.go_stripped'?: boolean | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.import_hash'?: string | undefined; 'process.pe.imports'?: unknown[] | undefined; 'process.pe.imports_names_entropy'?: string | number | undefined; 'process.pe.imports_names_var_entropy'?: string | number | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.session_leader.vpid'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.parent.vpid'?: 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.vpid'?: string | number | 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.capabilities.effective'?: string[] | undefined; 'process.thread.capabilities.permitted'?: 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.vpid'?: string | number | 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.go_import_hash'?: string | undefined; 'threat.indicator.file.elf.go_imports'?: unknown; 'threat.indicator.file.elf.go_imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.elf.go_imports_names_var_entropy'?: string | number | undefined; 'threat.indicator.file.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.go_import_hash'?: string | undefined; 'threat.indicator.file.pe.go_imports'?: unknown; 'threat.indicator.file.pe.go_imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.pe.go_imports_names_var_entropy'?: string | number | undefined; 'threat.indicator.file.pe.go_stripped'?: boolean | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.import_hash'?: string | undefined; 'threat.indicator.file.pe.imports'?: unknown[] | undefined; 'threat.indicator.file.pe.imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.pe.imports_names_var_entropy'?: string | number | 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.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.name'?: string | 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_status_updated_at'?: string | number | 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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: string[] | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; }) | ({} & { 'kibana.alert.datafeed_results'?: { datafeed_id?: string | undefined; datafeed_state?: string | undefined; job_id?: string | undefined; job_state?: string | undefined; }[] | undefined; 'kibana.alert.delayed_data_results'?: { annotation?: string | undefined; end_timestamp?: string | number | undefined; job_id?: string | undefined; missed_docs_count?: string | number | undefined; }[] | undefined; 'kibana.alert.job_errors_results'?: { errors?: unknown; job_id?: string | undefined; }[] | undefined; 'kibana.alert.mml_results'?: { job_id?: string | undefined; log_time?: string | number | undefined; memory_status?: string | undefined; model_bytes?: string | number | undefined; model_bytes_exceeded?: string | number | undefined; model_bytes_memory_limit?: string | number | undefined; peak_model_bytes?: string | 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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: string[] | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; }) | ({} & { 'kibana.alert.results'?: { description?: string | undefined; health_status?: string | undefined; issues?: unknown; node_name?: string | undefined; transform_id?: string | undefined; transform_state?: 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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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; 'container.id'?: string | undefined; 'error.grouping_key'?: string | undefined; 'error.grouping_name'?: string | undefined; 'host.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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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_status_updated_at'?: string | number | 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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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; 'container.security_context.privileged'?: boolean | 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.go_import_hash'?: string | undefined; 'dll.pe.go_imports'?: unknown; 'dll.pe.go_imports_names_entropy'?: string | number | undefined; 'dll.pe.go_imports_names_var_entropy'?: string | number | undefined; 'dll.pe.go_stripped'?: boolean | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.import_hash'?: string | undefined; 'dll.pe.imports'?: unknown[] | undefined; 'dll.pe.imports_names_entropy'?: string | number | undefined; 'dll.pe.imports_names_var_entropy'?: string | number | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dll.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.go_import_hash'?: string | undefined; 'file.elf.go_imports'?: unknown; 'file.elf.go_imports_names_entropy'?: string | number | undefined; 'file.elf.go_imports_names_var_entropy'?: string | number | undefined; 'file.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.imports_names_entropy'?: string | number | undefined; 'file.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.macho.go_import_hash'?: string | undefined; 'file.macho.go_imports'?: unknown; 'file.macho.go_imports_names_entropy'?: string | number | undefined; 'file.macho.go_imports_names_var_entropy'?: string | number | undefined; 'file.macho.go_stripped'?: boolean | undefined; 'file.macho.import_hash'?: string | undefined; 'file.macho.imports'?: unknown[] | undefined; 'file.macho.imports_names_entropy'?: string | number | undefined; 'file.macho.imports_names_var_entropy'?: string | number | undefined; 'file.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.macho.symhash'?: 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.go_import_hash'?: string | undefined; 'file.pe.go_imports'?: unknown; 'file.pe.go_imports_names_entropy'?: string | number | undefined; 'file.pe.go_imports_names_var_entropy'?: string | number | undefined; 'file.pe.go_stripped'?: boolean | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.import_hash'?: string | undefined; 'file.pe.imports'?: unknown[] | undefined; 'file.pe.imports_names_entropy'?: string | number | undefined; 'file.pe.imports_names_var_entropy'?: string | number | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.annotation'?: string[] | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.label'?: 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.go_import_hash'?: string | undefined; 'process.elf.go_imports'?: unknown; 'process.elf.go_imports_names_entropy'?: string | number | undefined; 'process.elf.go_imports_names_var_entropy'?: string | number | undefined; 'process.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.imports_names_entropy'?: string | number | undefined; 'process.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.session_leader.vpid'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.parent.vpid'?: 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.vpid'?: string | number | 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.vpid'?: string | number | 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.macho.go_import_hash'?: string | undefined; 'process.macho.go_imports'?: unknown; 'process.macho.go_imports_names_entropy'?: string | number | undefined; 'process.macho.go_imports_names_var_entropy'?: string | number | undefined; 'process.macho.go_stripped'?: boolean | undefined; 'process.macho.import_hash'?: string | undefined; 'process.macho.imports'?: unknown[] | undefined; 'process.macho.imports_names_entropy'?: string | number | undefined; 'process.macho.imports_names_var_entropy'?: string | number | undefined; 'process.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.macho.symhash'?: string | undefined; '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.go_import_hash'?: string | undefined; 'process.parent.elf.go_imports'?: unknown; 'process.parent.elf.go_imports_names_entropy'?: string | number | undefined; 'process.parent.elf.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.imports_names_entropy'?: string | number | undefined; 'process.parent.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.group_leader.vpid'?: 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.macho.go_import_hash'?: string | undefined; 'process.parent.macho.go_imports'?: unknown; 'process.parent.macho.go_imports_names_entropy'?: string | number | undefined; 'process.parent.macho.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.macho.go_stripped'?: boolean | undefined; 'process.parent.macho.import_hash'?: string | undefined; 'process.parent.macho.imports'?: unknown[] | undefined; 'process.parent.macho.imports_names_entropy'?: string | number | undefined; 'process.parent.macho.imports_names_var_entropy'?: string | number | undefined; 'process.parent.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.macho.symhash'?: string | 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.go_import_hash'?: string | undefined; 'process.parent.pe.go_imports'?: unknown; 'process.parent.pe.go_imports_names_entropy'?: string | number | undefined; 'process.parent.pe.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.pe.go_stripped'?: boolean | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.import_hash'?: string | undefined; 'process.parent.pe.imports'?: unknown[] | undefined; 'process.parent.pe.imports_names_entropy'?: string | number | undefined; 'process.parent.pe.imports_names_var_entropy'?: string | number | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.capabilities.effective'?: string[] | undefined; 'process.parent.thread.capabilities.permitted'?: 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.vpid'?: string | number | 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.go_import_hash'?: string | undefined; 'process.pe.go_imports'?: unknown; 'process.pe.go_imports_names_entropy'?: string | number | undefined; 'process.pe.go_imports_names_var_entropy'?: string | number | undefined; 'process.pe.go_stripped'?: boolean | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.import_hash'?: string | undefined; 'process.pe.imports'?: unknown[] | undefined; 'process.pe.imports_names_entropy'?: string | number | undefined; 'process.pe.imports_names_var_entropy'?: string | number | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.session_leader.vpid'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.parent.vpid'?: 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.vpid'?: string | number | 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.capabilities.effective'?: string[] | undefined; 'process.thread.capabilities.permitted'?: 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.vpid'?: string | number | 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.go_import_hash'?: string | undefined; 'threat.indicator.file.elf.go_imports'?: unknown; 'threat.indicator.file.elf.go_imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.elf.go_imports_names_var_entropy'?: string | number | undefined; 'threat.indicator.file.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.go_import_hash'?: string | undefined; 'threat.indicator.file.pe.go_imports'?: unknown; 'threat.indicator.file.pe.go_imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.pe.go_imports_names_var_entropy'?: string | number | undefined; 'threat.indicator.file.pe.go_stripped'?: boolean | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.import_hash'?: string | undefined; 'threat.indicator.file.pe.imports'?: unknown[] | undefined; 'threat.indicator.file.pe.imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.pe.imports_names_var_entropy'?: string | number | 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.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.name'?: string | 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_status_updated_at'?: string | number | 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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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; 'container.security_context.privileged'?: boolean | 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.go_import_hash'?: string | undefined; 'dll.pe.go_imports'?: unknown; 'dll.pe.go_imports_names_entropy'?: string | number | undefined; 'dll.pe.go_imports_names_var_entropy'?: string | number | undefined; 'dll.pe.go_stripped'?: boolean | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.import_hash'?: string | undefined; 'dll.pe.imports'?: unknown[] | undefined; 'dll.pe.imports_names_entropy'?: string | number | undefined; 'dll.pe.imports_names_var_entropy'?: string | number | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dll.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.go_import_hash'?: string | undefined; 'file.elf.go_imports'?: unknown; 'file.elf.go_imports_names_entropy'?: string | number | undefined; 'file.elf.go_imports_names_var_entropy'?: string | number | undefined; 'file.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.imports_names_entropy'?: string | number | undefined; 'file.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.macho.go_import_hash'?: string | undefined; 'file.macho.go_imports'?: unknown; 'file.macho.go_imports_names_entropy'?: string | number | undefined; 'file.macho.go_imports_names_var_entropy'?: string | number | undefined; 'file.macho.go_stripped'?: boolean | undefined; 'file.macho.import_hash'?: string | undefined; 'file.macho.imports'?: unknown[] | undefined; 'file.macho.imports_names_entropy'?: string | number | undefined; 'file.macho.imports_names_var_entropy'?: string | number | undefined; 'file.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.macho.symhash'?: 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.go_import_hash'?: string | undefined; 'file.pe.go_imports'?: unknown; 'file.pe.go_imports_names_entropy'?: string | number | undefined; 'file.pe.go_imports_names_var_entropy'?: string | number | undefined; 'file.pe.go_stripped'?: boolean | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.import_hash'?: string | undefined; 'file.pe.imports'?: unknown[] | undefined; 'file.pe.imports_names_entropy'?: string | number | undefined; 'file.pe.imports_names_var_entropy'?: string | number | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.annotation'?: string[] | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.label'?: 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.go_import_hash'?: string | undefined; 'process.elf.go_imports'?: unknown; 'process.elf.go_imports_names_entropy'?: string | number | undefined; 'process.elf.go_imports_names_var_entropy'?: string | number | undefined; 'process.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.imports_names_entropy'?: string | number | undefined; 'process.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.session_leader.vpid'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.parent.vpid'?: 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.vpid'?: string | number | 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.vpid'?: string | number | 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.macho.go_import_hash'?: string | undefined; 'process.macho.go_imports'?: unknown; 'process.macho.go_imports_names_entropy'?: string | number | undefined; 'process.macho.go_imports_names_var_entropy'?: string | number | undefined; 'process.macho.go_stripped'?: boolean | undefined; 'process.macho.import_hash'?: string | undefined; 'process.macho.imports'?: unknown[] | undefined; 'process.macho.imports_names_entropy'?: string | number | undefined; 'process.macho.imports_names_var_entropy'?: string | number | undefined; 'process.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.macho.symhash'?: string | undefined; '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.go_import_hash'?: string | undefined; 'process.parent.elf.go_imports'?: unknown; 'process.parent.elf.go_imports_names_entropy'?: string | number | undefined; 'process.parent.elf.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.imports_names_entropy'?: string | number | undefined; 'process.parent.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.group_leader.vpid'?: 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.macho.go_import_hash'?: string | undefined; 'process.parent.macho.go_imports'?: unknown; 'process.parent.macho.go_imports_names_entropy'?: string | number | undefined; 'process.parent.macho.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.macho.go_stripped'?: boolean | undefined; 'process.parent.macho.import_hash'?: string | undefined; 'process.parent.macho.imports'?: unknown[] | undefined; 'process.parent.macho.imports_names_entropy'?: string | number | undefined; 'process.parent.macho.imports_names_var_entropy'?: string | number | undefined; 'process.parent.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.macho.symhash'?: string | 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.go_import_hash'?: string | undefined; 'process.parent.pe.go_imports'?: unknown; 'process.parent.pe.go_imports_names_entropy'?: string | number | undefined; 'process.parent.pe.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.pe.go_stripped'?: boolean | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.import_hash'?: string | undefined; 'process.parent.pe.imports'?: unknown[] | undefined; 'process.parent.pe.imports_names_entropy'?: string | number | undefined; 'process.parent.pe.imports_names_var_entropy'?: string | number | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.capabilities.effective'?: string[] | undefined; 'process.parent.thread.capabilities.permitted'?: 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.vpid'?: string | number | 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.go_import_hash'?: string | undefined; 'process.pe.go_imports'?: unknown; 'process.pe.go_imports_names_entropy'?: string | number | undefined; 'process.pe.go_imports_names_var_entropy'?: string | number | undefined; 'process.pe.go_stripped'?: boolean | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.import_hash'?: string | undefined; 'process.pe.imports'?: unknown[] | undefined; 'process.pe.imports_names_entropy'?: string | number | undefined; 'process.pe.imports_names_var_entropy'?: string | number | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.session_leader.vpid'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.parent.vpid'?: 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.vpid'?: string | number | 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.capabilities.effective'?: string[] | undefined; 'process.thread.capabilities.permitted'?: 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.vpid'?: string | number | 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.go_import_hash'?: string | undefined; 'threat.indicator.file.elf.go_imports'?: unknown; 'threat.indicator.file.elf.go_imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.elf.go_imports_names_var_entropy'?: string | number | undefined; 'threat.indicator.file.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.go_import_hash'?: string | undefined; 'threat.indicator.file.pe.go_imports'?: unknown; 'threat.indicator.file.pe.go_imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.pe.go_imports_names_var_entropy'?: string | number | undefined; 'threat.indicator.file.pe.go_stripped'?: boolean | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.import_hash'?: string | undefined; 'threat.indicator.file.pe.imports'?: unknown[] | undefined; 'threat.indicator.file.pe.imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.pe.imports_names_var_entropy'?: string | number | 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.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.name'?: string | 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_status_updated_at'?: string | number | 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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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; 'container.security_context.privileged'?: boolean | 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.go_import_hash'?: string | undefined; 'dll.pe.go_imports'?: unknown; 'dll.pe.go_imports_names_entropy'?: string | number | undefined; 'dll.pe.go_imports_names_var_entropy'?: string | number | undefined; 'dll.pe.go_stripped'?: boolean | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.import_hash'?: string | undefined; 'dll.pe.imports'?: unknown[] | undefined; 'dll.pe.imports_names_entropy'?: string | number | undefined; 'dll.pe.imports_names_var_entropy'?: string | number | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dll.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.go_import_hash'?: string | undefined; 'file.elf.go_imports'?: unknown; 'file.elf.go_imports_names_entropy'?: string | number | undefined; 'file.elf.go_imports_names_var_entropy'?: string | number | undefined; 'file.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.imports_names_entropy'?: string | number | undefined; 'file.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.macho.go_import_hash'?: string | undefined; 'file.macho.go_imports'?: unknown; 'file.macho.go_imports_names_entropy'?: string | number | undefined; 'file.macho.go_imports_names_var_entropy'?: string | number | undefined; 'file.macho.go_stripped'?: boolean | undefined; 'file.macho.import_hash'?: string | undefined; 'file.macho.imports'?: unknown[] | undefined; 'file.macho.imports_names_entropy'?: string | number | undefined; 'file.macho.imports_names_var_entropy'?: string | number | undefined; 'file.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.macho.symhash'?: 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.go_import_hash'?: string | undefined; 'file.pe.go_imports'?: unknown; 'file.pe.go_imports_names_entropy'?: string | number | undefined; 'file.pe.go_imports_names_var_entropy'?: string | number | undefined; 'file.pe.go_stripped'?: boolean | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.import_hash'?: string | undefined; 'file.pe.imports'?: unknown[] | undefined; 'file.pe.imports_names_entropy'?: string | number | undefined; 'file.pe.imports_names_var_entropy'?: string | number | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.annotation'?: string[] | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.label'?: 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.go_import_hash'?: string | undefined; 'process.elf.go_imports'?: unknown; 'process.elf.go_imports_names_entropy'?: string | number | undefined; 'process.elf.go_imports_names_var_entropy'?: string | number | undefined; 'process.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.imports_names_entropy'?: string | number | undefined; 'process.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.session_leader.vpid'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.parent.vpid'?: 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.vpid'?: string | number | 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.vpid'?: string | number | 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.macho.go_import_hash'?: string | undefined; 'process.macho.go_imports'?: unknown; 'process.macho.go_imports_names_entropy'?: string | number | undefined; 'process.macho.go_imports_names_var_entropy'?: string | number | undefined; 'process.macho.go_stripped'?: boolean | undefined; 'process.macho.import_hash'?: string | undefined; 'process.macho.imports'?: unknown[] | undefined; 'process.macho.imports_names_entropy'?: string | number | undefined; 'process.macho.imports_names_var_entropy'?: string | number | undefined; 'process.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.macho.symhash'?: string | undefined; '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.go_import_hash'?: string | undefined; 'process.parent.elf.go_imports'?: unknown; 'process.parent.elf.go_imports_names_entropy'?: string | number | undefined; 'process.parent.elf.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.imports_names_entropy'?: string | number | undefined; 'process.parent.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.group_leader.vpid'?: 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.macho.go_import_hash'?: string | undefined; 'process.parent.macho.go_imports'?: unknown; 'process.parent.macho.go_imports_names_entropy'?: string | number | undefined; 'process.parent.macho.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.macho.go_stripped'?: boolean | undefined; 'process.parent.macho.import_hash'?: string | undefined; 'process.parent.macho.imports'?: unknown[] | undefined; 'process.parent.macho.imports_names_entropy'?: string | number | undefined; 'process.parent.macho.imports_names_var_entropy'?: string | number | undefined; 'process.parent.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.macho.symhash'?: string | 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.go_import_hash'?: string | undefined; 'process.parent.pe.go_imports'?: unknown; 'process.parent.pe.go_imports_names_entropy'?: string | number | undefined; 'process.parent.pe.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.pe.go_stripped'?: boolean | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.import_hash'?: string | undefined; 'process.parent.pe.imports'?: unknown[] | undefined; 'process.parent.pe.imports_names_entropy'?: string | number | undefined; 'process.parent.pe.imports_names_var_entropy'?: string | number | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.capabilities.effective'?: string[] | undefined; 'process.parent.thread.capabilities.permitted'?: 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.vpid'?: string | number | 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.go_import_hash'?: string | undefined; 'process.pe.go_imports'?: unknown; 'process.pe.go_imports_names_entropy'?: string | number | undefined; 'process.pe.go_imports_names_var_entropy'?: string | number | undefined; 'process.pe.go_stripped'?: boolean | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.import_hash'?: string | undefined; 'process.pe.imports'?: unknown[] | undefined; 'process.pe.imports_names_entropy'?: string | number | undefined; 'process.pe.imports_names_var_entropy'?: string | number | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.session_leader.vpid'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.parent.vpid'?: 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.vpid'?: string | number | 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.capabilities.effective'?: string[] | undefined; 'process.thread.capabilities.permitted'?: 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.vpid'?: string | number | 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.go_import_hash'?: string | undefined; 'threat.indicator.file.elf.go_imports'?: unknown; 'threat.indicator.file.elf.go_imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.elf.go_imports_names_var_entropy'?: string | number | undefined; 'threat.indicator.file.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.go_import_hash'?: string | undefined; 'threat.indicator.file.pe.go_imports'?: unknown; 'threat.indicator.file.pe.go_imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.pe.go_imports_names_var_entropy'?: string | number | undefined; 'threat.indicator.file.pe.go_stripped'?: boolean | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.import_hash'?: string | undefined; 'threat.indicator.file.pe.imports'?: unknown[] | undefined; 'threat.indicator.file.pe.imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.pe.imports_names_var_entropy'?: string | number | 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.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.name'?: string | 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_status_updated_at'?: string | number | undefined; 'kibana.alert.workflow_user'?: string | undefined; }) | ({} & { 'agent.name'?: string | undefined; 'anomaly.bucket_span.minutes'?: string | undefined; 'anomaly.start'?: string | number | undefined; configId?: string | undefined; 'error.message'?: string | undefined; 'error.stack_trace'?: string | undefined; 'host.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; 'location.id'?: string[] | undefined; 'location.name'?: string[] | undefined; 'monitor.id'?: string | undefined; 'monitor.name'?: string | undefined; 'monitor.state.id'?: string | undefined; 'monitor.tags'?: string[] | undefined; 'monitor.type'?: string | undefined; 'observer.geo.name'?: string[] | undefined; 'observer.name'?: string[] | undefined; 'service.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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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_status_updated_at'?: string | number | 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; 'event.original'?: string | undefined; 'host.asset.criticality'?: 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.consecutive_matches'?: string | number | 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.host.criticality_level'?: string | undefined; 'kibana.alert.intended_timestamp'?: string | 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.previous_action_group'?: 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.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: 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.severity_improving'?: boolean | 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.user.criticality_level'?: string | undefined; 'kibana.alert.workflow_assignee_ids'?: string[] | undefined; 'kibana.alert.workflow_reason'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_status_updated_at'?: string | number | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.alert.workflow_user'?: string | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; 'user.asset.criticality'?: 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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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; 'container.security_context.privileged'?: boolean | 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.go_import_hash'?: string | undefined; 'dll.pe.go_imports'?: unknown; 'dll.pe.go_imports_names_entropy'?: string | number | undefined; 'dll.pe.go_imports_names_var_entropy'?: string | number | undefined; 'dll.pe.go_stripped'?: boolean | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.import_hash'?: string | undefined; 'dll.pe.imports'?: unknown[] | undefined; 'dll.pe.imports_names_entropy'?: string | number | undefined; 'dll.pe.imports_names_var_entropy'?: string | number | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dll.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.go_import_hash'?: string | undefined; 'file.elf.go_imports'?: unknown; 'file.elf.go_imports_names_entropy'?: string | number | undefined; 'file.elf.go_imports_names_var_entropy'?: string | number | undefined; 'file.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.imports_names_entropy'?: string | number | undefined; 'file.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.macho.go_import_hash'?: string | undefined; 'file.macho.go_imports'?: unknown; 'file.macho.go_imports_names_entropy'?: string | number | undefined; 'file.macho.go_imports_names_var_entropy'?: string | number | undefined; 'file.macho.go_stripped'?: boolean | undefined; 'file.macho.import_hash'?: string | undefined; 'file.macho.imports'?: unknown[] | undefined; 'file.macho.imports_names_entropy'?: string | number | undefined; 'file.macho.imports_names_var_entropy'?: string | number | undefined; 'file.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.macho.symhash'?: 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.go_import_hash'?: string | undefined; 'file.pe.go_imports'?: unknown; 'file.pe.go_imports_names_entropy'?: string | number | undefined; 'file.pe.go_imports_names_var_entropy'?: string | number | undefined; 'file.pe.go_stripped'?: boolean | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.import_hash'?: string | undefined; 'file.pe.imports'?: unknown[] | undefined; 'file.pe.imports_names_entropy'?: string | number | undefined; 'file.pe.imports_names_var_entropy'?: string | number | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.annotation'?: string[] | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.label'?: 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.go_import_hash'?: string | undefined; 'process.elf.go_imports'?: unknown; 'process.elf.go_imports_names_entropy'?: string | number | undefined; 'process.elf.go_imports_names_var_entropy'?: string | number | undefined; 'process.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.imports_names_entropy'?: string | number | undefined; 'process.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.session_leader.vpid'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.parent.vpid'?: 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.vpid'?: string | number | 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.vpid'?: string | number | 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.macho.go_import_hash'?: string | undefined; 'process.macho.go_imports'?: unknown; 'process.macho.go_imports_names_entropy'?: string | number | undefined; 'process.macho.go_imports_names_var_entropy'?: string | number | undefined; 'process.macho.go_stripped'?: boolean | undefined; 'process.macho.import_hash'?: string | undefined; 'process.macho.imports'?: unknown[] | undefined; 'process.macho.imports_names_entropy'?: string | number | undefined; 'process.macho.imports_names_var_entropy'?: string | number | undefined; 'process.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.macho.symhash'?: string | undefined; '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.go_import_hash'?: string | undefined; 'process.parent.elf.go_imports'?: unknown; 'process.parent.elf.go_imports_names_entropy'?: string | number | undefined; 'process.parent.elf.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.imports_names_entropy'?: string | number | undefined; 'process.parent.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.group_leader.vpid'?: 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.macho.go_import_hash'?: string | undefined; 'process.parent.macho.go_imports'?: unknown; 'process.parent.macho.go_imports_names_entropy'?: string | number | undefined; 'process.parent.macho.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.macho.go_stripped'?: boolean | undefined; 'process.parent.macho.import_hash'?: string | undefined; 'process.parent.macho.imports'?: unknown[] | undefined; 'process.parent.macho.imports_names_entropy'?: string | number | undefined; 'process.parent.macho.imports_names_var_entropy'?: string | number | undefined; 'process.parent.macho.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.macho.symhash'?: string | 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.go_import_hash'?: string | undefined; 'process.parent.pe.go_imports'?: unknown; 'process.parent.pe.go_imports_names_entropy'?: string | number | undefined; 'process.parent.pe.go_imports_names_var_entropy'?: string | number | undefined; 'process.parent.pe.go_stripped'?: boolean | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.import_hash'?: string | undefined; 'process.parent.pe.imports'?: unknown[] | undefined; 'process.parent.pe.imports_names_entropy'?: string | number | undefined; 'process.parent.pe.imports_names_var_entropy'?: string | number | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.capabilities.effective'?: string[] | undefined; 'process.parent.thread.capabilities.permitted'?: 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.vpid'?: string | number | 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.go_import_hash'?: string | undefined; 'process.pe.go_imports'?: unknown; 'process.pe.go_imports_names_entropy'?: string | number | undefined; 'process.pe.go_imports_names_var_entropy'?: string | number | undefined; 'process.pe.go_stripped'?: boolean | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.import_hash'?: string | undefined; 'process.pe.imports'?: unknown[] | undefined; 'process.pe.imports_names_entropy'?: string | number | undefined; 'process.pe.imports_names_var_entropy'?: string | number | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.session_leader.vpid'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.parent.vpid'?: 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.vpid'?: string | number | 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.capabilities.effective'?: string[] | undefined; 'process.thread.capabilities.permitted'?: 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.vpid'?: string | number | 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.go_import_hash'?: string | undefined; 'threat.indicator.file.elf.go_imports'?: unknown; 'threat.indicator.file.elf.go_imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.elf.go_imports_names_var_entropy'?: string | number | undefined; 'threat.indicator.file.elf.go_stripped'?: boolean | 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.import_hash'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.elf.imports_names_var_entropy'?: string | number | 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; var_entropy?: string | number | 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.go_import_hash'?: string | undefined; 'threat.indicator.file.pe.go_imports'?: unknown; 'threat.indicator.file.pe.go_imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.pe.go_imports_names_var_entropy'?: string | number | undefined; 'threat.indicator.file.pe.go_stripped'?: boolean | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.import_hash'?: string | undefined; 'threat.indicator.file.pe.imports'?: unknown[] | undefined; 'threat.indicator.file.pe.imports_names_entropy'?: string | number | undefined; 'threat.indicator.file.pe.imports_names_var_entropy'?: string | number | 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.pe.sections'?: { entropy?: string | number | undefined; name?: string | undefined; physical_size?: string | number | undefined; var_entropy?: string | number | undefined; virtual_size?: string | number | undefined; }[] | 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.name'?: string | 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_status_updated_at'?: string | number | 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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: string[] | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; }) | ({} & { 'kibana.alert.datafeed_results'?: { datafeed_id?: string | undefined; datafeed_state?: string | undefined; job_id?: string | undefined; job_state?: string | undefined; }[] | undefined; 'kibana.alert.delayed_data_results'?: { annotation?: string | undefined; end_timestamp?: string | number | undefined; job_id?: string | undefined; missed_docs_count?: string | number | undefined; }[] | undefined; 'kibana.alert.job_errors_results'?: { errors?: unknown; job_id?: string | undefined; }[] | undefined; 'kibana.alert.mml_results'?: { job_id?: string | undefined; log_time?: string | number | undefined; memory_status?: string | undefined; model_bytes?: string | number | undefined; model_bytes_exceeded?: string | number | undefined; model_bytes_memory_limit?: string | number | undefined; peak_model_bytes?: string | 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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: string[] | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; }) | ({} & { 'kibana.alert.results'?: { description?: string | undefined; health_status?: string | undefined; issues?: unknown; node_name?: string | undefined; transform_id?: string | undefined; transform_state?: 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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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, @@ -420,7 +420,7 @@ "label": "ObservabilityUptimeAlert", "description": [], "signature": [ - "{} & { 'agent.name'?: string | undefined; 'anomaly.bucket_span.minutes'?: string | undefined; 'anomaly.start'?: string | number | undefined; configId?: string | undefined; 'error.message'?: string | undefined; 'host.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; 'location.id'?: string[] | undefined; 'location.name'?: string[] | undefined; 'monitor.id'?: string | undefined; 'monitor.name'?: string | undefined; 'monitor.state.id'?: string | undefined; 'monitor.tags'?: string[] | undefined; 'monitor.type'?: string | undefined; 'observer.geo.name'?: string[] | undefined; 'observer.name'?: string[] | undefined; 'service.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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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_status_updated_at'?: string | number | undefined; 'kibana.alert.workflow_user'?: string | undefined; }" + "{} & { 'agent.name'?: string | undefined; 'anomaly.bucket_span.minutes'?: string | undefined; 'anomaly.start'?: string | number | undefined; configId?: string | undefined; 'error.message'?: string | undefined; 'error.stack_trace'?: string | undefined; 'host.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; 'location.id'?: string[] | undefined; 'location.name'?: string[] | undefined; 'monitor.id'?: string | undefined; 'monitor.name'?: string | undefined; 'monitor.state.id'?: string | undefined; 'monitor.tags'?: string[] | undefined; 'monitor.type'?: string | undefined; 'observer.geo.name'?: string[] | undefined; 'observer.name'?: string[] | undefined; 'service.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; 'event.original'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.consecutive_matches'?: string | number | 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.intended_timestamp'?: string | number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.previous_action_group'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.timestamp'?: string | number | undefined; 'kibana.alert.rule.execution.type'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.severity_improving'?: boolean | 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_assignee_ids'?: 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_status_updated_at'?: string | number | undefined; 'kibana.alert.workflow_user'?: string | undefined; }" ], "path": "packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_uptime_schema.ts", "deprecated": false, diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index bceadb65a9666..050a127ec9c7f 100644 --- a/api_docs/kbn_alerts_as_data_utils.mdx +++ b/api_docs/kbn_alerts_as_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils title: "@kbn/alerts-as-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-as-data-utils plugin -date: 2024-10-29 +date: 2024-11-05 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_grouping.mdx b/api_docs/kbn_alerts_grouping.mdx index 82c65bf7575c1..865311c497aa3 100644 --- a/api_docs/kbn_alerts_grouping.mdx +++ b/api_docs/kbn_alerts_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-grouping title: "@kbn/alerts-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-grouping plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-grouping'] --- import kbnAlertsGroupingObj from './kbn_alerts_grouping.devdocs.json'; diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index 7fa8b377c6799..58438314f8a86 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2024-10-29 +date: 2024-11-05 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 c8cd4e19a10a6..d8b4928eadef6 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_collection_utils.mdx b/api_docs/kbn_analytics_collection_utils.mdx index 2e9eff036a373..02a036a6e0853 100644 --- a/api_docs/kbn_analytics_collection_utils.mdx +++ b/api_docs/kbn_analytics_collection_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-collection-utils title: "@kbn/analytics-collection-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-collection-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-collection-utils'] --- import kbnAnalyticsCollectionUtilsObj from './kbn_analytics_collection_utils.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 182006b9b0fb3..1c7460fe378cb 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_data_view.mdx b/api_docs/kbn_apm_data_view.mdx index 4eda73d3c66bf..abe04d9df84d6 100644 --- a/api_docs/kbn_apm_data_view.mdx +++ b/api_docs/kbn_apm_data_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-data-view title: "@kbn/apm-data-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-data-view plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-data-view'] --- import kbnApmDataViewObj from './kbn_apm_data_view.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index 3406142f5e795..5de6c9ef073e9 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2024-10-29 +date: 2024-11-05 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 d88fe6a140a67..6cbd9b9e1ae53 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2024-10-29 +date: 2024-11-05 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_types.mdx b/api_docs/kbn_apm_types.mdx index f0af3e9380940..8d63bdfb06505 100644 --- a/api_docs/kbn_apm_types.mdx +++ b/api_docs/kbn_apm_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-types title: "@kbn/apm-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-types plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-types'] --- import kbnApmTypesObj from './kbn_apm_types.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 0a1c8c51fbcd9..f5e7153bc06d7 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_avc_banner.mdx b/api_docs/kbn_avc_banner.mdx index d2c8222cc79eb..70ea4af44630b 100644 --- a/api_docs/kbn_avc_banner.mdx +++ b/api_docs/kbn_avc_banner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-avc-banner title: "@kbn/avc-banner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/avc-banner plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/avc-banner'] --- import kbnAvcBannerObj from './kbn_avc_banner.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index 0dfdad7c3d71e..345030e3a3367 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_bfetch_error.mdx b/api_docs/kbn_bfetch_error.mdx index c35da97cd6020..8c91cf2492cda 100644 --- a/api_docs/kbn_bfetch_error.mdx +++ b/api_docs/kbn_bfetch_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-bfetch-error title: "@kbn/bfetch-error" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/bfetch-error plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/bfetch-error'] --- import kbnBfetchErrorObj from './kbn_bfetch_error.devdocs.json'; diff --git a/api_docs/kbn_calculate_auto.mdx b/api_docs/kbn_calculate_auto.mdx index 03a259fda36be..1a2c9db3ab4ac 100644 --- a/api_docs/kbn_calculate_auto.mdx +++ b/api_docs/kbn_calculate_auto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-auto title: "@kbn/calculate-auto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-auto plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-auto'] --- import kbnCalculateAutoObj from './kbn_calculate_auto.devdocs.json'; diff --git a/api_docs/kbn_calculate_width_from_char_count.mdx b/api_docs/kbn_calculate_width_from_char_count.mdx index 42a9df9b72129..f3aeefe61eefd 100644 --- a/api_docs/kbn_calculate_width_from_char_count.mdx +++ b/api_docs/kbn_calculate_width_from_char_count.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-width-from-char-count title: "@kbn/calculate-width-from-char-count" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-width-from-char-count plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-width-from-char-count'] --- import kbnCalculateWidthFromCharCountObj from './kbn_calculate_width_from_char_count.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index d043b597f3df8..517ad1d81b596 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cbor.mdx b/api_docs/kbn_cbor.mdx index 6f965c6a20e39..ef097c72cbddb 100644 --- a/api_docs/kbn_cbor.mdx +++ b/api_docs/kbn_cbor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cbor title: "@kbn/cbor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cbor plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cbor'] --- import kbnCborObj from './kbn_cbor.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index 781fc3a265d65..315204a50b0db 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2024-10-29 +date: 2024-11-05 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 c9665525dcdb0..c6f0d947d57c0 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2024-10-29 +date: 2024-11-05 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 c35862a2ed808..81ad4cca13a91 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2024-10-29 +date: 2024-11-05 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 e70a1b8ead212..6ab31a81559b6 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2024-10-29 +date: 2024-11-05 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 53cc023be9953..fb2cd367d726f 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2024-10-29 +date: 2024-11-05 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 4efa45404e288..c14b10e7e0011 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2024-10-29 +date: 2024-11-05 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 60391a2ea2ffc..eb21eca60b779 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_cloud_security_posture.mdx b/api_docs/kbn_cloud_security_posture.mdx index d55ef9c63de40..a735474872de6 100644 --- a/api_docs/kbn_cloud_security_posture.mdx +++ b/api_docs/kbn_cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cloud-security-posture title: "@kbn/cloud-security-posture" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cloud-security-posture plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cloud-security-posture'] --- import kbnCloudSecurityPostureObj from './kbn_cloud_security_posture.devdocs.json'; diff --git a/api_docs/kbn_cloud_security_posture_common.mdx b/api_docs/kbn_cloud_security_posture_common.mdx index 177a584749022..fa1e5919f46ee 100644 --- a/api_docs/kbn_cloud_security_posture_common.mdx +++ b/api_docs/kbn_cloud_security_posture_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cloud-security-posture-common title: "@kbn/cloud-security-posture-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cloud-security-posture-common plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cloud-security-posture-common'] --- import kbnCloudSecurityPostureCommonObj from './kbn_cloud_security_posture_common.devdocs.json'; diff --git a/api_docs/kbn_cloud_security_posture_graph.mdx b/api_docs/kbn_cloud_security_posture_graph.mdx index 1cae4ff1c4038..c50bb068f6827 100644 --- a/api_docs/kbn_cloud_security_posture_graph.mdx +++ b/api_docs/kbn_cloud_security_posture_graph.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cloud-security-posture-graph title: "@kbn/cloud-security-posture-graph" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cloud-security-posture-graph plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cloud-security-posture-graph'] --- import kbnCloudSecurityPostureGraphObj from './kbn_cloud_security_posture_graph.devdocs.json'; diff --git a/api_docs/kbn_code_editor.mdx b/api_docs/kbn_code_editor.mdx index e276b4694d933..d96d3f80c74cd 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mock.mdx b/api_docs/kbn_code_editor_mock.mdx index 2dcc4e5ea40fc..c7a9581966097 100644 --- a/api_docs/kbn_code_editor_mock.mdx +++ b/api_docs/kbn_code_editor_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mock title: "@kbn/code-editor-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mock plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mock'] --- import kbnCodeEditorMockObj from './kbn_code_editor_mock.devdocs.json'; diff --git a/api_docs/kbn_code_owners.mdx b/api_docs/kbn_code_owners.mdx index 06a4e11fc96d3..cfc8000891d8d 100644 --- a/api_docs/kbn_code_owners.mdx +++ b/api_docs/kbn_code_owners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-owners title: "@kbn/code-owners" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-owners plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-owners'] --- import kbnCodeOwnersObj from './kbn_code_owners.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 981b7139df0e5..c4aac5800aa80 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2024-10-29 +date: 2024-11-05 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 31d25569cb32b..2702a9fd5f6e9 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] --- import kbnConfigObj from './kbn_config.devdocs.json'; diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index 3e7017a2d3151..ed8a9789e3049 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-mocks'] --- import kbnConfigMocksObj from './kbn_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_config_schema.mdx b/api_docs/kbn_config_schema.mdx index 068775331182c..d94f1ff13af97 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_editor.mdx b/api_docs/kbn_content_management_content_editor.mdx index 84ec09b9fc2bb..c27a84a82ef77 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-editor'] --- import kbnContentManagementContentEditorObj from './kbn_content_management_content_editor.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_insights_public.mdx b/api_docs/kbn_content_management_content_insights_public.mdx index af2683ea7fcd5..0d3add8edccf4 100644 --- a/api_docs/kbn_content_management_content_insights_public.mdx +++ b/api_docs/kbn_content_management_content_insights_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-insights-public title: "@kbn/content-management-content-insights-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-insights-public plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-insights-public'] --- import kbnContentManagementContentInsightsPublicObj from './kbn_content_management_content_insights_public.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_insights_server.mdx b/api_docs/kbn_content_management_content_insights_server.mdx index 61b76efd569b6..dcd23130af765 100644 --- a/api_docs/kbn_content_management_content_insights_server.mdx +++ b/api_docs/kbn_content_management_content_insights_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-insights-server title: "@kbn/content-management-content-insights-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-insights-server plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-insights-server'] --- import kbnContentManagementContentInsightsServerObj from './kbn_content_management_content_insights_server.devdocs.json'; diff --git a/api_docs/kbn_content_management_favorites_public.mdx b/api_docs/kbn_content_management_favorites_public.mdx index b298472d4a5fa..28d48a25c9e00 100644 --- a/api_docs/kbn_content_management_favorites_public.mdx +++ b/api_docs/kbn_content_management_favorites_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-favorites-public title: "@kbn/content-management-favorites-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-favorites-public plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-favorites-public'] --- import kbnContentManagementFavoritesPublicObj from './kbn_content_management_favorites_public.devdocs.json'; diff --git a/api_docs/kbn_content_management_favorites_server.mdx b/api_docs/kbn_content_management_favorites_server.mdx index 7719d502de90c..c7ca7c39de4c2 100644 --- a/api_docs/kbn_content_management_favorites_server.mdx +++ b/api_docs/kbn_content_management_favorites_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-favorites-server title: "@kbn/content-management-favorites-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-favorites-server plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-favorites-server'] --- import kbnContentManagementFavoritesServerObj from './kbn_content_management_favorites_server.devdocs.json'; diff --git a/api_docs/kbn_content_management_tabbed_table_list_view.mdx b/api_docs/kbn_content_management_tabbed_table_list_view.mdx index 9fa69123c265f..2518aeabf2560 100644 --- a/api_docs/kbn_content_management_tabbed_table_list_view.mdx +++ b/api_docs/kbn_content_management_tabbed_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-tabbed-table-list-view title: "@kbn/content-management-tabbed-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-tabbed-table-list-view plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-tabbed-table-list-view'] --- import kbnContentManagementTabbedTableListViewObj from './kbn_content_management_tabbed_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view.mdx b/api_docs/kbn_content_management_table_list_view.mdx index cf85a90fec809..92c217e454071 100644 --- a/api_docs/kbn_content_management_table_list_view.mdx +++ b/api_docs/kbn_content_management_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view title: "@kbn/content-management-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view'] --- import kbnContentManagementTableListViewObj from './kbn_content_management_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_common.mdx b/api_docs/kbn_content_management_table_list_view_common.mdx index 71392561db807..6db246664d2ab 100644 --- a/api_docs/kbn_content_management_table_list_view_common.mdx +++ b/api_docs/kbn_content_management_table_list_view_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-common title: "@kbn/content-management-table-list-view-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-common plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-common'] --- import kbnContentManagementTableListViewCommonObj from './kbn_content_management_table_list_view_common.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_table.mdx b/api_docs/kbn_content_management_table_list_view_table.mdx index 87a8d4b71bdf7..f96dfc69eb6b2 100644 --- a/api_docs/kbn_content_management_table_list_view_table.mdx +++ b/api_docs/kbn_content_management_table_list_view_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-table title: "@kbn/content-management-table-list-view-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-table plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-table'] --- import kbnContentManagementTableListViewTableObj from './kbn_content_management_table_list_view_table.devdocs.json'; diff --git a/api_docs/kbn_content_management_user_profiles.mdx b/api_docs/kbn_content_management_user_profiles.mdx index 99fee0b741ae8..c6e62819c9c52 100644 --- a/api_docs/kbn_content_management_user_profiles.mdx +++ b/api_docs/kbn_content_management_user_profiles.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-user-profiles title: "@kbn/content-management-user-profiles" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-user-profiles plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-user-profiles'] --- import kbnContentManagementUserProfilesObj from './kbn_content_management_user_profiles.devdocs.json'; diff --git a/api_docs/kbn_content_management_utils.mdx b/api_docs/kbn_content_management_utils.mdx index 96827dda84e5b..69a8d9f7e9aaf 100644 --- a/api_docs/kbn_content_management_utils.mdx +++ b/api_docs/kbn_content_management_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-utils title: "@kbn/content-management-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-utils plugin -date: 2024-10-29 +date: 2024-11-05 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.devdocs.json b/api_docs/kbn_core_analytics_browser.devdocs.json index 92a17abe08595..288f48a4955a0 100644 --- a/api_docs/kbn_core_analytics_browser.devdocs.json +++ b/api_docs/kbn_core_analytics_browser.devdocs.json @@ -722,10 +722,6 @@ "plugin": "elasticAssistant", "path": "x-pack/plugins/elastic_assistant/server/routes/attack_discovery/post/helpers/handle_graph_error/index.tsx" }, - { - "plugin": "elasticAssistant", - "path": "x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts" - }, { "plugin": "globalSearchBar", "path": "x-pack/plugins/global_search_bar/public/telemetry/event_reporter.ts" @@ -782,6 +778,30 @@ "plugin": "infra", "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_client.ts" }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_client.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_client.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_client.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_client.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" + }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" @@ -950,6 +970,22 @@ "plugin": "securitySolution", "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/entity_store/task/field_retention_enrichment_task.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/task/field_retention_enrichment_task.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts" + }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.ts" @@ -1418,6 +1454,38 @@ "plugin": "infra", "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts" }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts" + }, { "plugin": "inventory", "path": "x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_service.test.ts" diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index 4d0116c075393..3fbe56fe0cb89 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 d11ac459247a8..d8c140ecc82d9 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 a43bcc90795ab..92b9284c46275 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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.devdocs.json b/api_docs/kbn_core_analytics_server.devdocs.json index 32c4147c1080b..c08c5dec164f0 100644 --- a/api_docs/kbn_core_analytics_server.devdocs.json +++ b/api_docs/kbn_core_analytics_server.devdocs.json @@ -730,10 +730,6 @@ "plugin": "elasticAssistant", "path": "x-pack/plugins/elastic_assistant/server/routes/attack_discovery/post/helpers/handle_graph_error/index.tsx" }, - { - "plugin": "elasticAssistant", - "path": "x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts" - }, { "plugin": "globalSearchBar", "path": "x-pack/plugins/global_search_bar/public/telemetry/event_reporter.ts" @@ -790,6 +786,30 @@ "plugin": "infra", "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_client.ts" }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_client.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_client.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_client.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_client.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" + }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" @@ -958,6 +978,22 @@ "plugin": "securitySolution", "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/entity_store/task/field_retention_enrichment_task.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/task/field_retention_enrichment_task.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts" + }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.ts" @@ -1426,6 +1462,38 @@ "plugin": "infra", "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts" }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts" + }, { "plugin": "inventory", "path": "x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_service.test.ts" diff --git a/api_docs/kbn_core_analytics_server.mdx b/api_docs/kbn_core_analytics_server.mdx index 366b4a4f2774c..8e7967053c0cc 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2024-10-29 +date: 2024-11-05 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 2a102c0d53d2a..b959c5e140ecd 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 d83935c76e364..ccebe9d980a83 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 aa4fed77cf134..18c329859b443 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 62c91d0d8ee54..831f49645ed1b 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 e62870b10367d..b6e40c468c53c 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 aa5cd6c600e45..e5051164644d5 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2024-10-29 +date: 2024-11-05 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 6e08efd5599ad..fc1b41e732e65 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 0b0b9a88af9fe..eed8546dffc74 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 c15c7db9e9ebf..ece67b7da94a3 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 a41352d26b2cf..10323744150cc 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 2f29656d38d29..60baa95846623 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2024-10-29 +date: 2024-11-05 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 636323c7db0df..778cc1ebd4eb1 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 0b1d59a032ca7..ba20297680269 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 48f94d7bd9940..ceed04d90795e 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 23057607b7597..91d4d279bf172 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2024-10-29 +date: 2024-11-05 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 c011a18e7dbcd..bd359f6706587 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2024-10-29 +date: 2024-11-05 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 0392de50f71ee..8ab2495699f2d 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 9a5c873f29227..74cc5c0a5be1b 100644 --- a/api_docs/kbn_core_chrome_browser.devdocs.json +++ b/api_docs/kbn_core_chrome_browser.devdocs.json @@ -3259,6 +3259,20 @@ "deprecated": false, "trackAdoption": false, "children": [ + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-public.NavigationTreeDefinitionUI.id", + "type": "CompoundType", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "\"security\" | \"es\" | \"oblt\"" + ], + "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/core-chrome-browser", "id": "def-public.NavigationTreeDefinitionUI.body", @@ -3630,12 +3644,15 @@ { "parentPluginId": "@kbn/core-chrome-browser", "id": "def-public.SolutionNavigationDefinition.id", - "type": "string", + "type": "CompoundType", "tags": [], "label": "id", "description": [ "Unique id for the solution navigation." ], + "signature": [ + "\"security\" | \"es\" | \"oblt\"" + ], "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", "deprecated": false, "trackAdoption": false @@ -3736,50 +3753,6 @@ } ], "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/core-chrome-browser", - "id": "def-public.SolutionNavigationDefinitions", - "type": "Interface", - "tags": [], - "label": "SolutionNavigationDefinitions", - "description": [], - "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-chrome-browser", - "id": "def-public.SolutionNavigationDefinitions.Unnamed", - "type": "IndexSignature", - "tags": [], - "label": "[id: string]: SolutionNavigationDefinition", - "description": [], - "signature": [ - "[id: string]: ", - { - "pluginId": "@kbn/core-chrome-browser", - "scope": "public", - "docId": "kibKbnCoreChromeBrowserPluginApi", - "section": "def-public.SolutionNavigationDefinition", - "text": "SolutionNavigationDefinition" - }, - "<", - { - "pluginId": "@kbn/core-chrome-browser", - "scope": "public", - "docId": "kibKbnCoreChromeBrowserPluginApi", - "section": "def-public.AppDeepLinkId", - "text": "AppDeepLinkId" - }, - ">" - ], - "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false } ], "enums": [], @@ -3792,7 +3765,7 @@ "label": "AppDeepLinkId", "description": [], "signature": [ - "\"fleet\" | \"graph\" | \"ml\" | \"monitoring\" | \"profiling\" | \"metrics\" | \"management\" | \"apm\" | \"synthetics\" | \"ux\" | \"canvas\" | \"logs\" | \"dashboards\" | \"slo\" | \"observabilityAIAssistant\" | \"home\" | \"integrations\" | \"discover\" | \"observability-overview\" | \"appSearch\" | \"dev_tools\" | \"maps\" | \"visualize\" | \"dev_tools:console\" | \"dev_tools:searchprofiler\" | \"dev_tools:painless_lab\" | \"dev_tools:grokdebugger\" | \"ml:notifications\" | \"ml:nodes\" | \"ml:overview\" | \"ml:memoryUsage\" | \"ml:settings\" | \"ml:dataVisualizer\" | \"ml:logPatternAnalysis\" | \"ml:logRateAnalysis\" | \"ml:singleMetricViewer\" | \"ml:anomalyDetection\" | \"ml:anomalyExplorer\" | \"ml:dataDrift\" | \"ml:dataFrameAnalytics\" | \"ml:resultExplorer\" | \"ml:analyticsMap\" | \"ml:aiOps\" | \"ml:changePointDetections\" | \"ml:modelManagement\" | \"ml:nodesOverview\" | \"ml:esqlDataVisualizer\" | \"ml:fileUpload\" | \"ml:indexDataVisualizer\" | \"ml:calendarSettings\" | \"ml:filterListsSettings\" | \"ml:suppliedConfigurations\" | \"osquery\" | \"management:transform\" | \"management:watcher\" | \"management:cases\" | \"management:tags\" | \"management:maintenanceWindows\" | \"management:cross_cluster_replication\" | \"management:dataViews\" | \"management:spaces\" | \"management:settings\" | \"management:users\" | \"management:migrate_data\" | \"management:search_sessions\" | \"management:data_quality\" | \"management:filesManagement\" | \"management:roles\" | \"management:reporting\" | \"management:aiAssistantManagementSelection\" | \"management:securityAiAssistantManagement\" | \"management:observabilityAiAssistantManagement\" | \"management:api_keys\" | \"management:license_management\" | \"management:index_lifecycle_management\" | \"management:index_management\" | \"management:ingest_pipelines\" | \"management:jobsListLink\" | \"management:objects\" | \"management:pipelines\" | \"management:remote_clusters\" | \"management:role_mappings\" | \"management:rollup_jobs\" | \"management:snapshot_restore\" | \"management:triggersActions\" | \"management:triggersActionsConnectors\" | \"management:upgrade_assistant\" | \"enterpriseSearch\" | \"enterpriseSearchContent\" | \"enterpriseSearchApplications\" | \"searchInferenceEndpoints\" | \"enterpriseSearchAnalytics\" | \"workplaceSearch\" | \"serverlessElasticsearch\" | \"serverlessConnectors\" | \"searchPlayground\" | \"searchHomepage\" | \"enterpriseSearchContent:connectors\" | \"enterpriseSearchContent:searchIndices\" | \"enterpriseSearchContent:webCrawlers\" | \"enterpriseSearchApplications:searchApplications\" | \"enterpriseSearchApplications:playground\" | \"appSearch:engines\" | \"searchInferenceEndpoints:inferenceEndpoints\" | \"elasticsearchStart\" | \"elasticsearchIndices\" | \"observability-logs-explorer\" | \"last-used-logs-viewer\" | \"observabilityOnboarding\" | \"inventory\" | \"logs:settings\" | \"logs:stream\" | \"logs:log-categories\" | \"logs:anomalies\" | \"observability-overview:cases\" | \"observability-overview:alerts\" | \"observability-overview:rules\" | \"observability-overview:cases_create\" | \"observability-overview:cases_configure\" | \"metrics:settings\" | \"metrics:hosts\" | \"metrics:inventory\" | \"metrics:metrics-explorer\" | \"metrics:assetDetails\" | \"apm:services\" | \"apm:traces\" | \"apm:dependencies\" | \"apm:service-map\" | \"apm:settings\" | \"apm:service-groups-list\" | \"apm:storage-explorer\" | \"synthetics:overview\" | \"synthetics:certificates\" | \"profiling:functions\" | \"profiling:stacktraces\" | \"profiling:flamegraphs\" | \"inventory:datastreams\" | \"securitySolutionUI\" | \"securitySolutionUI:\" | \"securitySolutionUI:cases\" | \"securitySolutionUI:alerts\" | \"securitySolutionUI:rules\" | \"securitySolutionUI:policy\" | \"securitySolutionUI:overview\" | \"securitySolutionUI:dashboards\" | \"securitySolutionUI:kubernetes\" | \"securitySolutionUI:cases_create\" | \"securitySolutionUI:cases_configure\" | \"securitySolutionUI:hosts\" | \"securitySolutionUI:users\" | \"securitySolutionUI:cloud_defend-policies\" | \"securitySolutionUI:cloud_security_posture-dashboard\" | \"securitySolutionUI:cloud_security_posture-findings\" | \"securitySolutionUI:cloud_security_posture-benchmarks\" | \"securitySolutionUI:network\" | \"securitySolutionUI:data_quality\" | \"securitySolutionUI:explore\" | \"securitySolutionUI:assets\" | \"securitySolutionUI:cloud_defend\" | \"securitySolutionUI:notes\" | \"securitySolutionUI:administration\" | \"securitySolutionUI:attack_discovery\" | \"securitySolutionUI:blocklist\" | \"securitySolutionUI:cloud_security_posture-rules\" | \"securitySolutionUI:detections\" | \"securitySolutionUI:detection_response\" | \"securitySolutionUI:endpoints\" | \"securitySolutionUI:event_filters\" | \"securitySolutionUI:exceptions\" | \"securitySolutionUI:host_isolation_exceptions\" | \"securitySolutionUI:hosts-all\" | \"securitySolutionUI:hosts-anomalies\" | \"securitySolutionUI:hosts-risk\" | \"securitySolutionUI:hosts-events\" | \"securitySolutionUI:hosts-sessions\" | \"securitySolutionUI:hosts-uncommon_processes\" | \"securitySolutionUI:investigations\" | \"securitySolutionUI:get_started\" | \"securitySolutionUI:machine_learning-landing\" | \"securitySolutionUI:network-anomalies\" | \"securitySolutionUI:network-dns\" | \"securitySolutionUI:network-events\" | \"securitySolutionUI:network-flows\" | \"securitySolutionUI:network-http\" | \"securitySolutionUI:network-tls\" | \"securitySolutionUI:response_actions_history\" | \"securitySolutionUI:rules-add\" | \"securitySolutionUI:rules-create\" | \"securitySolutionUI:rules-landing\" | \"securitySolutionUI:threat_intelligence\" | \"securitySolutionUI:timelines\" | \"securitySolutionUI:timelines-templates\" | \"securitySolutionUI:trusted_apps\" | \"securitySolutionUI:users-all\" | \"securitySolutionUI:users-anomalies\" | \"securitySolutionUI:users-authentications\" | \"securitySolutionUI:users-events\" | \"securitySolutionUI:users-risk\" | \"securitySolutionUI:entity_analytics\" | \"securitySolutionUI:entity_analytics-management\" | \"securitySolutionUI:entity_analytics-asset-classification\" | \"securitySolutionUI:entity_analytics-entity_store_management\" | \"securitySolutionUI:coverage-overview\" | \"fleet:settings\" | \"fleet:agents\" | \"fleet:policies\" | \"fleet:data_streams\" | \"fleet:enrollment_tokens\" | \"fleet:uninstall_tokens\"" + "\"fleet\" | \"graph\" | \"ml\" | \"monitoring\" | \"profiling\" | \"metrics\" | \"management\" | \"apm\" | \"synthetics\" | \"ux\" | \"canvas\" | \"logs\" | \"dashboards\" | \"slo\" | \"observabilityAIAssistant\" | \"home\" | \"integrations\" | \"discover\" | \"observability-overview\" | \"appSearch\" | \"dev_tools\" | \"maps\" | \"visualize\" | \"dev_tools:console\" | \"dev_tools:searchprofiler\" | \"dev_tools:painless_lab\" | \"dev_tools:grokdebugger\" | \"ml:notifications\" | \"ml:nodes\" | \"ml:overview\" | \"ml:memoryUsage\" | \"ml:settings\" | \"ml:dataVisualizer\" | \"ml:logPatternAnalysis\" | \"ml:logRateAnalysis\" | \"ml:singleMetricViewer\" | \"ml:anomalyDetection\" | \"ml:anomalyExplorer\" | \"ml:dataDrift\" | \"ml:dataFrameAnalytics\" | \"ml:resultExplorer\" | \"ml:analyticsMap\" | \"ml:aiOps\" | \"ml:changePointDetections\" | \"ml:modelManagement\" | \"ml:nodesOverview\" | \"ml:esqlDataVisualizer\" | \"ml:fileUpload\" | \"ml:indexDataVisualizer\" | \"ml:calendarSettings\" | \"ml:filterListsSettings\" | \"ml:suppliedConfigurations\" | \"osquery\" | \"management:transform\" | \"management:watcher\" | \"management:cases\" | \"management:tags\" | \"management:maintenanceWindows\" | \"management:cross_cluster_replication\" | \"management:dataViews\" | \"management:spaces\" | \"management:settings\" | \"management:users\" | \"management:migrate_data\" | \"management:search_sessions\" | \"management:data_quality\" | \"management:filesManagement\" | \"management:roles\" | \"management:reporting\" | \"management:aiAssistantManagementSelection\" | \"management:securityAiAssistantManagement\" | \"management:observabilityAiAssistantManagement\" | \"management:api_keys\" | \"management:license_management\" | \"management:index_lifecycle_management\" | \"management:index_management\" | \"management:ingest_pipelines\" | \"management:jobsListLink\" | \"management:objects\" | \"management:pipelines\" | \"management:remote_clusters\" | \"management:role_mappings\" | \"management:rollup_jobs\" | \"management:snapshot_restore\" | \"management:triggersActions\" | \"management:triggersActionsConnectors\" | \"management:upgrade_assistant\" | \"enterpriseSearch\" | \"enterpriseSearchContent\" | \"enterpriseSearchApplications\" | \"searchInferenceEndpoints\" | \"enterpriseSearchAnalytics\" | \"workplaceSearch\" | \"serverlessElasticsearch\" | \"serverlessConnectors\" | \"searchPlayground\" | \"searchHomepage\" | \"enterpriseSearchContent:connectors\" | \"enterpriseSearchContent:searchIndices\" | \"enterpriseSearchContent:webCrawlers\" | \"enterpriseSearchApplications:searchApplications\" | \"enterpriseSearchApplications:playground\" | \"appSearch:engines\" | \"searchInferenceEndpoints:inferenceEndpoints\" | \"elasticsearchStart\" | \"elasticsearchIndices\" | \"enterpriseSearchElasticsearch\" | \"enterpriseSearchVectorSearch\" | \"enterpriseSearchSemanticSearch\" | \"enterpriseSearchAISearch\" | \"observability-logs-explorer\" | \"last-used-logs-viewer\" | \"observabilityOnboarding\" | \"inventory\" | \"logs:settings\" | \"logs:stream\" | \"logs:log-categories\" | \"logs:anomalies\" | \"observability-overview:cases\" | \"observability-overview:alerts\" | \"observability-overview:rules\" | \"observability-overview:cases_create\" | \"observability-overview:cases_configure\" | \"metrics:settings\" | \"metrics:hosts\" | \"metrics:inventory\" | \"metrics:metrics-explorer\" | \"metrics:assetDetails\" | \"apm:services\" | \"apm:traces\" | \"apm:dependencies\" | \"apm:service-map\" | \"apm:settings\" | \"apm:service-groups-list\" | \"apm:storage-explorer\" | \"synthetics:overview\" | \"synthetics:certificates\" | \"profiling:functions\" | \"profiling:stacktraces\" | \"profiling:flamegraphs\" | \"inventory:datastreams\" | \"securitySolutionUI\" | \"securitySolutionUI:\" | \"securitySolutionUI:cases\" | \"securitySolutionUI:alerts\" | \"securitySolutionUI:rules\" | \"securitySolutionUI:policy\" | \"securitySolutionUI:overview\" | \"securitySolutionUI:dashboards\" | \"securitySolutionUI:kubernetes\" | \"securitySolutionUI:cases_create\" | \"securitySolutionUI:cases_configure\" | \"securitySolutionUI:hosts\" | \"securitySolutionUI:users\" | \"securitySolutionUI:cloud_defend-policies\" | \"securitySolutionUI:cloud_security_posture-dashboard\" | \"securitySolutionUI:cloud_security_posture-findings\" | \"securitySolutionUI:cloud_security_posture-benchmarks\" | \"securitySolutionUI:network\" | \"securitySolutionUI:data_quality\" | \"securitySolutionUI:explore\" | \"securitySolutionUI:assets\" | \"securitySolutionUI:cloud_defend\" | \"securitySolutionUI:notes\" | \"securitySolutionUI:administration\" | \"securitySolutionUI:attack_discovery\" | \"securitySolutionUI:blocklist\" | \"securitySolutionUI:cloud_security_posture-rules\" | \"securitySolutionUI:detections\" | \"securitySolutionUI:detection_response\" | \"securitySolutionUI:endpoints\" | \"securitySolutionUI:event_filters\" | \"securitySolutionUI:exceptions\" | \"securitySolutionUI:host_isolation_exceptions\" | \"securitySolutionUI:hosts-all\" | \"securitySolutionUI:hosts-anomalies\" | \"securitySolutionUI:hosts-risk\" | \"securitySolutionUI:hosts-events\" | \"securitySolutionUI:hosts-sessions\" | \"securitySolutionUI:hosts-uncommon_processes\" | \"securitySolutionUI:investigations\" | \"securitySolutionUI:get_started\" | \"securitySolutionUI:machine_learning-landing\" | \"securitySolutionUI:network-anomalies\" | \"securitySolutionUI:network-dns\" | \"securitySolutionUI:network-events\" | \"securitySolutionUI:network-flows\" | \"securitySolutionUI:network-http\" | \"securitySolutionUI:network-tls\" | \"securitySolutionUI:response_actions_history\" | \"securitySolutionUI:rules-add\" | \"securitySolutionUI:rules-create\" | \"securitySolutionUI:rules-landing\" | \"securitySolutionUI:threat_intelligence\" | \"securitySolutionUI:timelines\" | \"securitySolutionUI:timelines-templates\" | \"securitySolutionUI:trusted_apps\" | \"securitySolutionUI:users-all\" | \"securitySolutionUI:users-anomalies\" | \"securitySolutionUI:users-authentications\" | \"securitySolutionUI:users-events\" | \"securitySolutionUI:users-risk\" | \"securitySolutionUI:entity_analytics\" | \"securitySolutionUI:entity_analytics-management\" | \"securitySolutionUI:entity_analytics-asset-classification\" | \"securitySolutionUI:entity_analytics-entity_store_management\" | \"securitySolutionUI:coverage-overview\" | \"fleet:settings\" | \"fleet:agents\" | \"fleet:policies\" | \"fleet:data_streams\" | \"fleet:enrollment_tokens\" | \"fleet:uninstall_tokens\"" ], "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", "deprecated": false, @@ -4226,6 +4199,84 @@ "deprecated": false, "trackAdoption": false, "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-public.SolutionId", + "type": "Type", + "tags": [], + "label": "SolutionId", + "description": [], + "signature": [ + "\"security\" | \"es\" | \"oblt\"" + ], + "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-public.SolutionNavigationDefinitions", + "type": "Type", + "tags": [], + "label": "SolutionNavigationDefinitions", + "description": [], + "signature": [ + "{ security?: ", + { + "pluginId": "@kbn/core-chrome-browser", + "scope": "public", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-public.SolutionNavigationDefinition", + "text": "SolutionNavigationDefinition" + }, + "<", + { + "pluginId": "@kbn/core-chrome-browser", + "scope": "public", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-public.AppDeepLinkId", + "text": "AppDeepLinkId" + }, + "> | undefined; es?: ", + { + "pluginId": "@kbn/core-chrome-browser", + "scope": "public", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-public.SolutionNavigationDefinition", + "text": "SolutionNavigationDefinition" + }, + "<", + { + "pluginId": "@kbn/core-chrome-browser", + "scope": "public", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-public.AppDeepLinkId", + "text": "AppDeepLinkId" + }, + "> | undefined; oblt?: ", + { + "pluginId": "@kbn/core-chrome-browser", + "scope": "public", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-public.SolutionNavigationDefinition", + "text": "SolutionNavigationDefinition" + }, + "<", + { + "pluginId": "@kbn/core-chrome-browser", + "scope": "public", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-public.AppDeepLinkId", + "text": "AppDeepLinkId" + }, + "> | undefined; }" + ], + "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false } ], "objects": [] diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index 095accbf9e41c..99633ad5c448d 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 | |-------------------|-----------|------------------------|-----------------| -| 210 | 0 | 103 | 0 | +| 211 | 0 | 104 | 0 | ## Client diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index a52f2b2c65f01..2cb6786a02040 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 6364f5141d39d..d9d619ed48aa7 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 c2c52bd89b23a..7981c7b5d0cf0 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 707315221ba6f..8d8fff22f076c 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 5586a541ca15f..6835256a88415 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 61ec8b24cbd66..ce3cc0bdae9bf 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2024-10-29 +date: 2024-11-05 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 4546e7ee7ee6f..adee2c9b5abae 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2024-10-29 +date: 2024-11-05 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 0df2ad9d8b05e..b8adad7c595e5 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 1bc79dbee7965..9c5ab8625eea8 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 50fe5b59f806f..fcbb5468a36d7 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 59088d2378285..376a530f4ba10 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 13fbe158b46f0..07271f012d139 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 bd4aa2f08f82e..879be3d22adba 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2024-10-29 +date: 2024-11-05 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 0fca2be853995..863fcf9fbb44d 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2024-10-29 +date: 2024-11-05 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 4366891b4fc97..0f0e87b13b028 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 d4717ec4edea7..9641ffc9fcf23 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 11a05adf90088..0552e4497d7e7 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 179e00647dbd5..1d61222897f85 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 39ea58f948c05..d2cf59f10abb7 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2024-10-29 +date: 2024-11-05 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 92e03b9252491..003bcb53bb579 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 f2e6383e027d7..46b9d6127d339 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 eec1068685a1c..611a2c9790245 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 64842f8376cf1..0e7068bbddfc6 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2024-10-29 +date: 2024-11-05 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 4491e4424310a..79794d3eb0d84 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 0c7cd3c1468a2..7640b5f0c0b29 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 a812c8d0e0ceb..ebb9ac89dbca4 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 29edca4d5cd34..df14d0335e664 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 f5cb72986da07..46d90613dfb75 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 a9224f980173d..54c4296c26748 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 4e2e9475d4454..44a9381616546 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 fa7996d7dbc66..9b0e1d7cd2511 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2024-10-29 +date: 2024-11-05 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 b8103461bfcd1..fadb0ff75230a 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2024-10-29 +date: 2024-11-05 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 a30697fb7b754..9133efd54092d 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 000c92523e6d3..98835744bb91a 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 439d53c428b83..d904279e0c57e 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 e1624b96c30a6..f902f5e832ab1 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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_feature_flags_browser.mdx b/api_docs/kbn_core_feature_flags_browser.mdx index 4b4a2a140ff1f..285dc9ee6b74a 100644 --- a/api_docs/kbn_core_feature_flags_browser.mdx +++ b/api_docs/kbn_core_feature_flags_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-browser title: "@kbn/core-feature-flags-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-browser plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-browser'] --- import kbnCoreFeatureFlagsBrowserObj from './kbn_core_feature_flags_browser.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_browser_internal.mdx b/api_docs/kbn_core_feature_flags_browser_internal.mdx index e2940c9b3f984..9a1f6d3bc2283 100644 --- a/api_docs/kbn_core_feature_flags_browser_internal.mdx +++ b/api_docs/kbn_core_feature_flags_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-browser-internal title: "@kbn/core-feature-flags-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-browser-internal plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-browser-internal'] --- import kbnCoreFeatureFlagsBrowserInternalObj from './kbn_core_feature_flags_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_browser_mocks.mdx b/api_docs/kbn_core_feature_flags_browser_mocks.mdx index 6924f840fc366..c27a0194df375 100644 --- a/api_docs/kbn_core_feature_flags_browser_mocks.mdx +++ b/api_docs/kbn_core_feature_flags_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-browser-mocks title: "@kbn/core-feature-flags-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-browser-mocks'] --- import kbnCoreFeatureFlagsBrowserMocksObj from './kbn_core_feature_flags_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_server.mdx b/api_docs/kbn_core_feature_flags_server.mdx index d45fabaffe6d1..e84893c655dac 100644 --- a/api_docs/kbn_core_feature_flags_server.mdx +++ b/api_docs/kbn_core_feature_flags_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-server title: "@kbn/core-feature-flags-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-server plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-server'] --- import kbnCoreFeatureFlagsServerObj from './kbn_core_feature_flags_server.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_server_internal.mdx b/api_docs/kbn_core_feature_flags_server_internal.mdx index 6a4a22b3aaf46..91d2b69f5c059 100644 --- a/api_docs/kbn_core_feature_flags_server_internal.mdx +++ b/api_docs/kbn_core_feature_flags_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-server-internal title: "@kbn/core-feature-flags-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-server-internal'] --- import kbnCoreFeatureFlagsServerInternalObj from './kbn_core_feature_flags_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_server_mocks.mdx b/api_docs/kbn_core_feature_flags_server_mocks.mdx index 3e6ab1b12a63a..aa8218e1896dd 100644 --- a/api_docs/kbn_core_feature_flags_server_mocks.mdx +++ b/api_docs/kbn_core_feature_flags_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-server-mocks title: "@kbn/core-feature-flags-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-server-mocks'] --- import kbnCoreFeatureFlagsServerMocksObj from './kbn_core_feature_flags_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index c41988b7ff4cd..80786b66ed41b 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 0543973c020a3..66dda94404091 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 8c0e79e187d21..f18bb15fb2e3a 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 64417fe82a75b..b30d645984752 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2024-10-29 +date: 2024-11-05 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 26a9f4c1d293f..135f59ee2c748 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 c1f14e9d7da5c..b6a13821b72f8 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2024-10-29 +date: 2024-11-05 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 6df002bf56282..1ded0859657aa 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2024-10-29 +date: 2024-11-05 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 ca93668fbc314..2dae0698964ee 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 0b3977ab557e7..69560e373e231 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 0d4d1023bca17..df4482c982cb1 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 b1c0475a014bc..8719d9f09c51d 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 35bd18cec9bde..47ac8af6a7ffb 100644 --- a/api_docs/kbn_core_http_server.devdocs.json +++ b/api_docs/kbn_core_http_server.devdocs.json @@ -3891,18 +3891,6 @@ "plugin": "actions", "path": "x-pack/plugins/actions/server/routes/connector/get/get.ts" }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/get_all.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/get.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/list_action_types.ts" - }, { "plugin": "share", "path": "src/plugins/share/server/url_service/http/short_urls/register_get_route.ts" @@ -5415,54 +5403,6 @@ "plugin": "triggersActionsUi", "path": "x-pack/plugins/triggers_actions_ui/server/routes/config.test.ts" }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/get.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/get.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/get.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/get.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/get_all.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/get_all.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/get_all.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/get_all.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/list_action_types.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/list_action_types.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/list_action_types.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/list_action_types.test.ts" - }, { "plugin": "alerting", "path": "x-pack/plugins/alerting/server/routes/legacy/find.test.ts" @@ -5783,6 +5723,10 @@ "plugin": "alerting", "path": "x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/find_maintenance_windows_route.test.ts" }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/find_maintenance_windows_route.test.ts" + }, { "plugin": "alerting", "path": "x-pack/plugins/alerting/server/routes/maintenance_window/apis/get/get_maintenance_window_route.test.ts" @@ -6569,14 +6513,6 @@ "plugin": "actions", "path": "x-pack/plugins/actions/server/routes/get_oauth_access_token.ts" }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/create.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/execute.ts" - }, { "plugin": "actions", "path": "x-pack/plugins/actions/server/routes/get_global_execution_logs.ts" @@ -7869,46 +7805,6 @@ "plugin": "ruleRegistry", "path": "x-pack/plugins/rule_registry/server/routes/__mocks__/server.ts" }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/create.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/create.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/create.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/create.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/execute.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/execute.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/execute.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/execute.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/execute.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/execute.test.ts" - }, { "plugin": "alerting", "path": "x-pack/plugins/alerting/server/routes/legacy/create.test.ts" @@ -8795,10 +8691,6 @@ "plugin": "actions", "path": "x-pack/plugins/actions/server/routes/connector/update/update.ts" }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/update.ts" - }, { "plugin": "alerting", "path": "x-pack/plugins/alerting/server/routes/legacy/update.ts" @@ -9179,26 +9071,6 @@ "plugin": "ruleRegistry", "path": "x-pack/plugins/rule_registry/server/routes/__mocks__/server.ts" }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/update.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/update.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/update.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/update.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/update.test.ts" - }, { "plugin": "alerting", "path": "x-pack/plugins/alerting/server/routes/legacy/update.test.ts" @@ -9807,10 +9679,6 @@ "plugin": "actions", "path": "x-pack/plugins/actions/server/routes/connector/delete/delete.ts" }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/delete.ts" - }, { "plugin": "share", "path": "src/plugins/share/server/url_service/http/short_urls/register_delete_route.ts" @@ -10095,22 +9963,6 @@ "plugin": "ruleRegistry", "path": "x-pack/plugins/rule_registry/server/routes/__mocks__/server.ts" }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/delete.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/delete.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/delete.test.ts" - }, - { - "plugin": "actions", - "path": "x-pack/plugins/actions/server/routes/legacy/delete.test.ts" - }, { "plugin": "alerting", "path": "x-pack/plugins/alerting/server/routes/legacy/delete.test.ts" @@ -16310,10 +16162,6 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.ts" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.ts" @@ -16342,6 +16190,10 @@ "plugin": "uptime", "path": "x-pack/plugins/observability_solution/uptime/server/legacy_uptime/uptime_server.ts" }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.ts" + }, { "plugin": "@kbn/core-http-router-server-internal", "path": "packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts" @@ -17181,14 +17033,6 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_delete_rules/route.ts" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.ts" @@ -17437,6 +17281,14 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/stats.ts" }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_delete_rules/route.ts" + }, { "plugin": "controls", "path": "src/plugins/controls/server/options_list/options_list_suggestions_route.ts" @@ -17636,10 +17488,6 @@ "plugin": "infra", "path": "x-pack/plugins/observability_solution/infra/server/lib/adapters/framework/kibana_framework_adapter.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.ts" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.ts" @@ -17660,6 +17508,10 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/timeline/routes/pinned_events/persist_pinned_event.ts" }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.ts" + }, { "plugin": "@kbn/core-http-router-server-internal", "path": "packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts" @@ -17895,10 +17747,6 @@ "plugin": "osquery", "path": "x-pack/plugins/osquery/server/routes/pack/delete_pack_route.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_delete_rules/route.ts" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/delete_rule/route.ts" @@ -17943,6 +17791,10 @@ "plugin": "synthetics", "path": "x-pack/plugins/observability_solution/synthetics/server/server.ts" }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_delete_rules/route.ts" + }, { "plugin": "@kbn/core-http-router-server-internal", "path": "packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts" diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index 4042198303dd3..6ba5c45d4db60 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index e93732cc43219..cb1bdc95d4b47 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] --- import kbnCoreHttpServerInternalObj from './kbn_core_http_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index 78e32544360ce..2da1a3ec6027f 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 3f3ab00f63557..74b2073532ff9 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 85f2da20c2277..be676dc05245a 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 3d50ec756f73b..f2ff9bdb6e36c 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2024-10-29 +date: 2024-11-05 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 a494c518e35a4..4b9f881a44f69 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 141c5ad53f6b5..7db15480d225c 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 2210651f3c64c..1b458340d2486 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 625a771e4ea28..e0f1dfdc61cd0 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 0c195e9b30877..1ef72819a8099 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 fa39be40e9fa8..b83b929721599 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 49bc20d23a4b6..667cda6fb6215 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 4a7255b1aa63b..9e859972cbdc0 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2024-10-29 +date: 2024-11-05 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 e59e56edc980c..1f063ac85cd29 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 a873b1414c2dd..b56ce1ecf29c2 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 3b507c030ce0a..adb44256bf9a9 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 c3bb906114f82..718808bc8fd89 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2024-10-29 +date: 2024-11-05 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 85d8223b8bbef..d4dd90326defb 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 e61cbf90885fe..c27b62fb2e3c6 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 668c3b0866955..5ca7af963f4c2 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 460f8875d9b5f..007b09e41fac6 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 4a7b0ba52266c..c0997045ceaae 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2024-10-29 +date: 2024-11-05 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 44c4365f283f3..74c7ddb144e80 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 4eaeae4dd8775..0e7a80c1985d8 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 0c108f0c5230f..4fd14dcf4534f 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 0a04fa7c50030..fb5abd4950149 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2024-10-29 +date: 2024-11-05 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 4f5167d64e03b..55dc181d76880 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 ea257ef78b210..b7df422cfe3bf 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 5fdb07bf4a4cc..6475bac99b4a5 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 9fc3d9337f5cf..51d16b8a9149d 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 d6f1b3b382efc..2e944a848f543 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 282bacb4109e5..d38137bd5fc51 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 181fb312a0c3c..444e3c48b2d1d 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 5ec7a2a8d9992..e7c50095f7605 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 f070cf9e294c8..bae4b985218f4 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 82076c6b15c7f..05c0d97005368 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 db2431856e501..e7ecafc0935ad 100644 --- a/api_docs/kbn_core_plugins_contracts_browser.mdx +++ b/api_docs/kbn_core_plugins_contracts_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-browser title: "@kbn/core-plugins-contracts-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 458ff7ad9a879..748c8975ea5e1 100644 --- a/api_docs/kbn_core_plugins_contracts_server.mdx +++ b/api_docs/kbn_core_plugins_contracts_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-server title: "@kbn/core-plugins-contracts-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-server plugin -date: 2024-10-29 +date: 2024-11-05 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 797af58f6db2c..b45e16a0b2b8f 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2024-10-29 +date: 2024-11-05 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 eea9b7056f214..8d99aa9770eeb 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 bf3f89fbb1e34..d8859f3808c8c 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2024-10-29 +date: 2024-11-05 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 0c8ea5fd20cb8..b88d84b4903d4 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 915007b9d16ac..2172f06678a02 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 cc5978246c6aa..eb8583c74de08 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 9c919ab9abfe0..2b1466e7bcbfc 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 fb59ccb60759b..1921754e3de5f 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 496d86f7d1d69..939135f9971d0 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-browser'] --- import kbnCoreSavedObjectsApiBrowserObj from './kbn_core_saved_objects_api_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index c35af813edae2..ce839d7769549 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index 0fffab87bb986..b55660ed8f279 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 e9e58d2978b15..e8fd0678aed29 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 a99fda4e40e71..763a7de132e4d 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 5d8ab5e269391..df898fd75958e 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 58815a50b8103..df6518c1bcebe 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 047ede1b85f75..a2d2801a77323 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 5221fd91127d8..894f50be83ebd 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2024-10-29 +date: 2024-11-05 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 1e4f35df588fb..a34b28a867b01 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 bcf3b3436b98b..4447d3fab92a0 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 43195cb8049bc..614fd45e1f9e3 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 03bb7db061c4e..d795ecd2811fd 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 99fa62021f0a1..fc280bfdd6ca3 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2024-10-29 +date: 2024-11-05 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 ccc6081f35c3b..afafb032030f2 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 d2c321edee7c4..59a29ad072677 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 b2f29567f72ee..0efe11962ef89 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser.mdx b/api_docs/kbn_core_security_browser.mdx index 4e1114736d538..5e344dd791047 100644 --- a/api_docs/kbn_core_security_browser.mdx +++ b/api_docs/kbn_core_security_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser title: "@kbn/core-security-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser'] --- import kbnCoreSecurityBrowserObj from './kbn_core_security_browser.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_internal.mdx b/api_docs/kbn_core_security_browser_internal.mdx index 728bad5e433a2..39dff54b2bb35 100644 --- a/api_docs/kbn_core_security_browser_internal.mdx +++ b/api_docs/kbn_core_security_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-internal title: "@kbn/core-security-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-internal plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-internal'] --- import kbnCoreSecurityBrowserInternalObj from './kbn_core_security_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_mocks.mdx b/api_docs/kbn_core_security_browser_mocks.mdx index a8c38f255cde6..d888846743061 100644 --- a/api_docs/kbn_core_security_browser_mocks.mdx +++ b/api_docs/kbn_core_security_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-mocks title: "@kbn/core-security-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-mocks'] --- import kbnCoreSecurityBrowserMocksObj from './kbn_core_security_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_security_common.mdx b/api_docs/kbn_core_security_common.mdx index efe1cde114a01..4b04c40416e1f 100644 --- a/api_docs/kbn_core_security_common.mdx +++ b/api_docs/kbn_core_security_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-common title: "@kbn/core-security-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-common plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-common'] --- import kbnCoreSecurityCommonObj from './kbn_core_security_common.devdocs.json'; diff --git a/api_docs/kbn_core_security_server.mdx b/api_docs/kbn_core_security_server.mdx index 04530829625c9..fe8c488929631 100644 --- a/api_docs/kbn_core_security_server.mdx +++ b/api_docs/kbn_core_security_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server title: "@kbn/core-security-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server'] --- import kbnCoreSecurityServerObj from './kbn_core_security_server.devdocs.json'; diff --git a/api_docs/kbn_core_security_server_internal.mdx b/api_docs/kbn_core_security_server_internal.mdx index ee437a0ae5135..5fceb18879350 100644 --- a/api_docs/kbn_core_security_server_internal.mdx +++ b/api_docs/kbn_core_security_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-internal title: "@kbn/core-security-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-internal'] --- import kbnCoreSecurityServerInternalObj from './kbn_core_security_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_server_mocks.mdx b/api_docs/kbn_core_security_server_mocks.mdx index 3fa65e7a5d1c7..8b9330c72cc7e 100644 --- a/api_docs/kbn_core_security_server_mocks.mdx +++ b/api_docs/kbn_core_security_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-mocks title: "@kbn/core-security-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-mocks'] --- import kbnCoreSecurityServerMocksObj from './kbn_core_security_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index b9b1d1a7bec19..f63b094e6ad7c 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2024-10-29 +date: 2024-11-05 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 82c4ddd3fcf3e..717b069630f68 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 4540224ee16c6..a51277e1d3a49 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2024-10-29 +date: 2024-11-05 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 9836fba612bcc..444b82671c712 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 5ca9ad5096cf9..3158b1b1349c6 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 4a2dea87ffe42..d7d00ffaaf55a 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2024-10-29 +date: 2024-11-05 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 dcfdcc1403243..c6cca95ca8352 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 487067f7b604e..8db368d51d3da 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2024-10-29 +date: 2024-11-05 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 0753177a23046..5434a44577afd 100644 --- a/api_docs/kbn_core_test_helpers_model_versions.mdx +++ b/api_docs/kbn_core_test_helpers_model_versions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-model-versions title: "@kbn/core-test-helpers-model-versions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-model-versions plugin -date: 2024-10-29 +date: 2024-11-05 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 b2a516e289d18..9ef841a1e6891 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2024-10-29 +date: 2024-11-05 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 e8c44c8b15d60..e499ff1b5819f 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2024-10-29 +date: 2024-11-05 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 3dfd8dcf53759..bea0dfa52b15a 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 b342d9e6b8bf0..b23b4efdb681d 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 5dbf7bdd40ce3..4960c9d5a2582 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 65aa80512df99..30e7fb2a2dfe2 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 6744459d778e2..0b9ec9af368f4 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 695ddd0e9e5f0..6fda6f1f41510 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2024-10-29 +date: 2024-11-05 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 8ff09a556e7e8..91f1497d4a7f0 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2024-10-29 +date: 2024-11-05 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 5aa0e599b0772..e35b13f059cdf 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 c8caa95cd5b31..d15b6aec58c09 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 5a89eb7b2c8fa..f7d38e1c8fbf2 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2024-10-29 +date: 2024-11-05 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 b3d06c2fd6f96..921f6a6f17600 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 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 963992129288d..2b65f8d0290ed 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser.mdx b/api_docs/kbn_core_user_profile_browser.mdx index 7490e44269008..641d42b8b3fcd 100644 --- a/api_docs/kbn_core_user_profile_browser.mdx +++ b/api_docs/kbn_core_user_profile_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser title: "@kbn/core-user-profile-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser'] --- import kbnCoreUserProfileBrowserObj from './kbn_core_user_profile_browser.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_internal.mdx b/api_docs/kbn_core_user_profile_browser_internal.mdx index a18408dddb18e..061c118f5c64e 100644 --- a/api_docs/kbn_core_user_profile_browser_internal.mdx +++ b/api_docs/kbn_core_user_profile_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-internal title: "@kbn/core-user-profile-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-internal plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-internal'] --- import kbnCoreUserProfileBrowserInternalObj from './kbn_core_user_profile_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_mocks.mdx b/api_docs/kbn_core_user_profile_browser_mocks.mdx index 7ea430c0986bc..c58812d6cde34 100644 --- a/api_docs/kbn_core_user_profile_browser_mocks.mdx +++ b/api_docs/kbn_core_user_profile_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-mocks title: "@kbn/core-user-profile-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-mocks plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-mocks'] --- import kbnCoreUserProfileBrowserMocksObj from './kbn_core_user_profile_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_common.mdx b/api_docs/kbn_core_user_profile_common.mdx index 3bc35aa30193a..d69496a17206d 100644 --- a/api_docs/kbn_core_user_profile_common.mdx +++ b/api_docs/kbn_core_user_profile_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-common title: "@kbn/core-user-profile-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-common plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-common'] --- import kbnCoreUserProfileCommonObj from './kbn_core_user_profile_common.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server.mdx b/api_docs/kbn_core_user_profile_server.mdx index 42bc5c720f400..2e33a944773c1 100644 --- a/api_docs/kbn_core_user_profile_server.mdx +++ b/api_docs/kbn_core_user_profile_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server title: "@kbn/core-user-profile-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server'] --- import kbnCoreUserProfileServerObj from './kbn_core_user_profile_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_internal.mdx b/api_docs/kbn_core_user_profile_server_internal.mdx index a6a58d8dfa45e..6709c228d2933 100644 --- a/api_docs/kbn_core_user_profile_server_internal.mdx +++ b/api_docs/kbn_core_user_profile_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-internal title: "@kbn/core-user-profile-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-internal plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-internal'] --- import kbnCoreUserProfileServerInternalObj from './kbn_core_user_profile_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_mocks.mdx b/api_docs/kbn_core_user_profile_server_mocks.mdx index 162ef32536274..7a6a687d16336 100644 --- a/api_docs/kbn_core_user_profile_server_mocks.mdx +++ b/api_docs/kbn_core_user_profile_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-mocks title: "@kbn/core-user-profile-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-mocks'] --- import kbnCoreUserProfileServerMocksObj from './kbn_core_user_profile_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server.mdx b/api_docs/kbn_core_user_settings_server.mdx index dfc55f8b9cb82..18d74fbad5d37 100644 --- a/api_docs/kbn_core_user_settings_server.mdx +++ b/api_docs/kbn_core_user_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server title: "@kbn/core-user-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server'] --- import kbnCoreUserSettingsServerObj from './kbn_core_user_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_mocks.mdx b/api_docs/kbn_core_user_settings_server_mocks.mdx index 91c144eabf626..33695332ae657 100644 --- a/api_docs/kbn_core_user_settings_server_mocks.mdx +++ b/api_docs/kbn_core_user_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-mocks title: "@kbn/core-user-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 0d3e667764e8d..9ecb9d49fbbb1 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2024-10-29 +date: 2024-11-05 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 7c4119c08e000..c65653a4f5b39 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2024-10-29 +date: 2024-11-05 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 56cc7f070330f..aa64562475335 100644 --- a/api_docs/kbn_custom_icons.mdx +++ b/api_docs/kbn_custom_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-icons title: "@kbn/custom-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-icons plugin -date: 2024-10-29 +date: 2024-11-05 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 7cfabc30ac9ba..1ddf803fc346e 100644 --- a/api_docs/kbn_custom_integrations.mdx +++ b/api_docs/kbn_custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-integrations title: "@kbn/custom-integrations" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-integrations plugin -date: 2024-10-29 +date: 2024-11-05 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 dbdc0ba6b37d0..13bad6f463d96 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_data_forge.mdx b/api_docs/kbn_data_forge.mdx index 287ac74639556..18b938a4ceafc 100644 --- a/api_docs/kbn_data_forge.mdx +++ b/api_docs/kbn_data_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-forge title: "@kbn/data-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-forge plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-forge'] --- import kbnDataForgeObj from './kbn_data_forge.devdocs.json'; diff --git a/api_docs/kbn_data_service.mdx b/api_docs/kbn_data_service.mdx index 0cb97d1fc056e..e30bf8f40ea8c 100644 --- a/api_docs/kbn_data_service.mdx +++ b/api_docs/kbn_data_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-service title: "@kbn/data-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-service plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-service'] --- import kbnDataServiceObj from './kbn_data_service.devdocs.json'; diff --git a/api_docs/kbn_data_stream_adapter.devdocs.json b/api_docs/kbn_data_stream_adapter.devdocs.json index 0cd9d1c79b2f8..1893c1919bcbd 100644 --- a/api_docs/kbn_data_stream_adapter.devdocs.json +++ b/api_docs/kbn_data_stream_adapter.devdocs.json @@ -1109,7 +1109,7 @@ "section": "def-common.MultiField", "text": "MultiField" }, - "[] | undefined; index?: boolean | undefined; path?: string | undefined; scaling_factor?: number | undefined; dynamic?: boolean | \"strict\" | undefined; properties?: Record | undefined; }; }" + "[] | undefined; index?: boolean | undefined; path?: string | undefined; scaling_factor?: number | undefined; dynamic?: boolean | \"strict\" | undefined; properties?: Record | undefined; inference_id?: string | undefined; copy_to?: string | undefined; }; }" ], "path": "packages/kbn-data-stream-adapter/src/data_stream_adapter.ts", "deprecated": false, @@ -1248,7 +1248,7 @@ "section": "def-common.MultiField", "text": "MultiField" }, - "[] | undefined; index?: boolean | undefined; path?: string | undefined; scaling_factor?: number | undefined; dynamic?: boolean | \"strict\" | undefined; properties?: Record | undefined; }; }" + "[] | undefined; index?: boolean | undefined; path?: string | undefined; scaling_factor?: number | undefined; dynamic?: boolean | \"strict\" | undefined; properties?: Record | undefined; inference_id?: string | undefined; copy_to?: string | undefined; }; }" ], "path": "packages/kbn-data-stream-adapter/src/field_maps/ecs_field_map.ts", "deprecated": false, @@ -1271,7 +1271,7 @@ "section": "def-common.MultiField", "text": "MultiField" }, - "[] | undefined; index?: boolean | undefined; path?: string | undefined; scaling_factor?: number | undefined; dynamic?: boolean | \"strict\" | undefined; properties?: Record | undefined; }; }" + "[] | undefined; index?: boolean | undefined; path?: string | undefined; scaling_factor?: number | undefined; dynamic?: boolean | \"strict\" | undefined; properties?: Record | undefined; inference_id?: string | undefined; copy_to?: string | undefined; }; }" ], "path": "packages/kbn-data-stream-adapter/src/field_maps/types.ts", "deprecated": false, @@ -1319,7 +1319,7 @@ "section": "def-common.MultiField", "text": "MultiField" }, - "[] | undefined; index?: boolean | undefined; path?: string | undefined; scaling_factor?: number | undefined; dynamic?: boolean | \"strict\" | undefined; properties?: Record | undefined; }; }" + "[] | undefined; index?: boolean | undefined; path?: string | undefined; scaling_factor?: number | undefined; dynamic?: boolean | \"strict\" | undefined; properties?: Record | undefined; inference_id?: string | undefined; copy_to?: string | undefined; }; }" ], "path": "packages/kbn-data-stream-adapter/src/field_maps/ecs_field_map.ts", "deprecated": false, diff --git a/api_docs/kbn_data_stream_adapter.mdx b/api_docs/kbn_data_stream_adapter.mdx index 287a9f6f6ef18..acd49bb1c9c3c 100644 --- a/api_docs/kbn_data_stream_adapter.mdx +++ b/api_docs/kbn_data_stream_adapter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-stream-adapter title: "@kbn/data-stream-adapter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-stream-adapter plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-stream-adapter'] --- import kbnDataStreamAdapterObj from './kbn_data_stream_adapter.devdocs.json'; diff --git a/api_docs/kbn_data_view_utils.mdx b/api_docs/kbn_data_view_utils.mdx index 7a2d0681c5b31..4dd5a7c70687f 100644 --- a/api_docs/kbn_data_view_utils.mdx +++ b/api_docs/kbn_data_view_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-view-utils title: "@kbn/data-view-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-view-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-view-utils'] --- import kbnDataViewUtilsObj from './kbn_data_view_utils.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index d14bb6a0244d9..b64177f3aa1ef 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2024-10-29 +date: 2024-11-05 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 1604383907995..f153840fd9a5b 100644 --- a/api_docs/kbn_deeplinks_analytics.mdx +++ b/api_docs/kbn_deeplinks_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-analytics title: "@kbn/deeplinks-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-analytics plugin -date: 2024-10-29 +date: 2024-11-05 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 5ee97e6714adb..a04f96d4495b7 100644 --- a/api_docs/kbn_deeplinks_devtools.mdx +++ b/api_docs/kbn_deeplinks_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-devtools title: "@kbn/deeplinks-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-devtools plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-devtools'] --- import kbnDeeplinksDevtoolsObj from './kbn_deeplinks_devtools.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_fleet.mdx b/api_docs/kbn_deeplinks_fleet.mdx index bd92656605e87..4c1d88110dd29 100644 --- a/api_docs/kbn_deeplinks_fleet.mdx +++ b/api_docs/kbn_deeplinks_fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-fleet title: "@kbn/deeplinks-fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-fleet plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-fleet'] --- import kbnDeeplinksFleetObj from './kbn_deeplinks_fleet.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_management.mdx b/api_docs/kbn_deeplinks_management.mdx index f0d265b7896a2..b9a42ee5d6c72 100644 --- a/api_docs/kbn_deeplinks_management.mdx +++ b/api_docs/kbn_deeplinks_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-management title: "@kbn/deeplinks-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-management plugin -date: 2024-10-29 +date: 2024-11-05 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 55fa5afb059c6..b40dfc2d05b5e 100644 --- a/api_docs/kbn_deeplinks_ml.mdx +++ b/api_docs/kbn_deeplinks_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-ml title: "@kbn/deeplinks-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-ml plugin -date: 2024-10-29 +date: 2024-11-05 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 2f1d0ad8347d2..4c09fc62f297c 100644 --- a/api_docs/kbn_deeplinks_observability.mdx +++ b/api_docs/kbn_deeplinks_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-observability title: "@kbn/deeplinks-observability" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-observability plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-observability'] --- import kbnDeeplinksObservabilityObj from './kbn_deeplinks_observability.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_search.devdocs.json b/api_docs/kbn_deeplinks_search.devdocs.json index 8376c52ebf1ea..d8cd4e993a4e4 100644 --- a/api_docs/kbn_deeplinks_search.devdocs.json +++ b/api_docs/kbn_deeplinks_search.devdocs.json @@ -30,7 +30,7 @@ "label": "DeepLinkId", "description": [], "signature": [ - "\"appSearch\" | \"enterpriseSearch\" | \"enterpriseSearchContent\" | \"enterpriseSearchApplications\" | \"searchInferenceEndpoints\" | \"enterpriseSearchAnalytics\" | \"workplaceSearch\" | \"serverlessElasticsearch\" | \"serverlessConnectors\" | \"searchPlayground\" | \"searchHomepage\" | \"enterpriseSearchContent:connectors\" | \"enterpriseSearchContent:searchIndices\" | \"enterpriseSearchContent:webCrawlers\" | \"enterpriseSearchApplications:searchApplications\" | \"enterpriseSearchApplications:playground\" | \"appSearch:engines\" | \"searchInferenceEndpoints:inferenceEndpoints\" | \"elasticsearchStart\" | \"elasticsearchIndices\"" + "\"appSearch\" | \"enterpriseSearch\" | \"enterpriseSearchContent\" | \"enterpriseSearchApplications\" | \"searchInferenceEndpoints\" | \"enterpriseSearchAnalytics\" | \"workplaceSearch\" | \"serverlessElasticsearch\" | \"serverlessConnectors\" | \"searchPlayground\" | \"searchHomepage\" | \"enterpriseSearchContent:connectors\" | \"enterpriseSearchContent:searchIndices\" | \"enterpriseSearchContent:webCrawlers\" | \"enterpriseSearchApplications:searchApplications\" | \"enterpriseSearchApplications:playground\" | \"appSearch:engines\" | \"searchInferenceEndpoints:inferenceEndpoints\" | \"elasticsearchStart\" | \"elasticsearchIndices\" | \"enterpriseSearchElasticsearch\" | \"enterpriseSearchVectorSearch\" | \"enterpriseSearchSemanticSearch\" | \"enterpriseSearchAISearch\"" ], "path": "packages/deeplinks/search/deep_links.ts", "deprecated": false, @@ -232,6 +232,66 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/deeplinks-search", + "id": "def-common.SEARCH_AI_SEARCH", + "type": "string", + "tags": [], + "label": "SEARCH_AI_SEARCH", + "description": [], + "signature": [ + "\"enterpriseSearchAISearch\"" + ], + "path": "packages/deeplinks/search/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/deeplinks-search", + "id": "def-common.SEARCH_ELASTICSEARCH", + "type": "string", + "tags": [], + "label": "SEARCH_ELASTICSEARCH", + "description": [], + "signature": [ + "\"enterpriseSearchElasticsearch\"" + ], + "path": "packages/deeplinks/search/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/deeplinks-search", + "id": "def-common.SEARCH_SEMANTIC_SEARCH", + "type": "string", + "tags": [], + "label": "SEARCH_SEMANTIC_SEARCH", + "description": [], + "signature": [ + "\"enterpriseSearchSemanticSearch\"" + ], + "path": "packages/deeplinks/search/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/deeplinks-search", + "id": "def-common.SEARCH_VECTOR_SEARCH", + "type": "string", + "tags": [], + "label": "SEARCH_VECTOR_SEARCH", + "description": [], + "signature": [ + "\"enterpriseSearchVectorSearch\"" + ], + "path": "packages/deeplinks/search/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/deeplinks-search", "id": "def-common.SERVERLESS_ES_APP_ID", diff --git a/api_docs/kbn_deeplinks_search.mdx b/api_docs/kbn_deeplinks_search.mdx index 28a86d5a0e836..e30c4ba755528 100644 --- a/api_docs/kbn_deeplinks_search.mdx +++ b/api_docs/kbn_deeplinks_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-search title: "@kbn/deeplinks-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-search plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-search'] --- import kbnDeeplinksSearchObj from './kbn_deeplinks_search.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-ki | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 17 | 0 | 17 | 0 | +| 21 | 0 | 21 | 0 | ## Common diff --git a/api_docs/kbn_deeplinks_security.mdx b/api_docs/kbn_deeplinks_security.mdx index 4b1ea7277f9a7..0bc0c75772836 100644 --- a/api_docs/kbn_deeplinks_security.mdx +++ b/api_docs/kbn_deeplinks_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-security title: "@kbn/deeplinks-security" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-security plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-security'] --- import kbnDeeplinksSecurityObj from './kbn_deeplinks_security.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_shared.mdx b/api_docs/kbn_deeplinks_shared.mdx index d98fc711c1839..c1dd02ede7f8c 100644 --- a/api_docs/kbn_deeplinks_shared.mdx +++ b/api_docs/kbn_deeplinks_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-shared title: "@kbn/deeplinks-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-shared plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-shared'] --- import kbnDeeplinksSharedObj from './kbn_deeplinks_shared.devdocs.json'; diff --git a/api_docs/kbn_default_nav_analytics.mdx b/api_docs/kbn_default_nav_analytics.mdx index 95abcf71ade98..88d7e4eab49b1 100644 --- a/api_docs/kbn_default_nav_analytics.mdx +++ b/api_docs/kbn_default_nav_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-analytics title: "@kbn/default-nav-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-analytics plugin -date: 2024-10-29 +date: 2024-11-05 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 6daad6284ec74..6f7a316ba6e5e 100644 --- a/api_docs/kbn_default_nav_devtools.mdx +++ b/api_docs/kbn_default_nav_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-devtools title: "@kbn/default-nav-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-devtools plugin -date: 2024-10-29 +date: 2024-11-05 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 b864e65c1ae9e..adeba017b974e 100644 --- a/api_docs/kbn_default_nav_management.mdx +++ b/api_docs/kbn_default_nav_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-management title: "@kbn/default-nav-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-management plugin -date: 2024-10-29 +date: 2024-11-05 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 7ec2e27238a8a..ad07159e4a6d3 100644 --- a/api_docs/kbn_default_nav_ml.mdx +++ b/api_docs/kbn_default_nav_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-ml title: "@kbn/default-nav-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-ml plugin -date: 2024-10-29 +date: 2024-11-05 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 95682ec66e9ed..c1dc9f3b4c67f 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2024-10-29 +date: 2024-11-05 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 5d291a852e35e..6c349754cd0d4 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2024-10-29 +date: 2024-11-05 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 44c6a2c823862..affb84e133f69 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2024-10-29 +date: 2024-11-05 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 e3f53e3853e78..d3be92557402b 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_discover_contextual_components.mdx b/api_docs/kbn_discover_contextual_components.mdx index 48cc7f9040395..43741f228b63a 100644 --- a/api_docs/kbn_discover_contextual_components.mdx +++ b/api_docs/kbn_discover_contextual_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-discover-contextual-components title: "@kbn/discover-contextual-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/discover-contextual-components plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/discover-contextual-components'] --- import kbnDiscoverContextualComponentsObj from './kbn_discover_contextual_components.devdocs.json'; diff --git a/api_docs/kbn_discover_utils.devdocs.json b/api_docs/kbn_discover_utils.devdocs.json index 25a8aaaf454cb..96845d5017c89 100644 --- a/api_docs/kbn_discover_utils.devdocs.json +++ b/api_docs/kbn_discover_utils.devdocs.json @@ -17,7 +17,266 @@ "objects": [] }, "common": { - "classes": [], + "classes": [ + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuRegistry", + "type": "Class", + "tags": [], + "label": "AppMenuRegistry", + "description": [], + "path": "packages/kbn-discover-utils/src/components/app_menu/app_menu_registry.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuRegistry.CUSTOM_ITEMS_LIMIT", + "type": "number", + "tags": [], + "label": "CUSTOM_ITEMS_LIMIT", + "description": [], + "path": "packages/kbn-discover-utils/src/components/app_menu/app_menu_registry.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuRegistry.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/app_menu_registry.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuRegistry.Unnamed.$1", + "type": "Array", + "tags": [], + "label": "primaryAndSecondaryActions", + "description": [], + "signature": [ + "(", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionPrimary", + "text": "AppMenuActionPrimary" + }, + " | ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuItemSecondary", + "text": "AppMenuItemSecondary" + }, + ")[]" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/app_menu_registry.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuRegistry.isActionRegistered", + "type": "Function", + "tags": [], + "label": "isActionRegistered", + "description": [], + "signature": [ + "(appMenuItemId: string) => boolean" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/app_menu_registry.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuRegistry.isActionRegistered.$1", + "type": "string", + "tags": [], + "label": "appMenuItemId", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/app_menu_registry.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuRegistry.registerCustomAction", + "type": "Function", + "tags": [], + "label": "registerCustomAction", + "description": [ + "\nRegister a custom action to the app menu. It can be a simple action or a submenu with more actions and horizontal rules.\nNote: Only 2 top level custom actions are allowed to be rendered in the app menu. The rest will be ignored.\nA custom action can also open a flyout or a modal. For that, return your custom react node from action's `onClick` event and call `onFinishAction` when you're done." + ], + "signature": [ + "(appMenuItem: ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuItemCustom", + "text": "AppMenuItemCustom" + }, + ") => void" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/app_menu_registry.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuRegistry.registerCustomAction.$1", + "type": "CompoundType", + "tags": [], + "label": "appMenuItem", + "description": [], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuItemCustom", + "text": "AppMenuItemCustom" + } + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/app_menu_registry.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuRegistry.registerCustomActionUnderSubmenu", + "type": "Function", + "tags": [], + "label": "registerCustomActionUnderSubmenu", + "description": [ + "\nRegister a custom action under a submenu. It can be an action or a horizontal rule.\nAny number of submenu actions can be registered and rendered.\nYou can also extend an existing submenu with more actions. For example, AppMenuActionType.alerts.\n`order` property is optional and can be used to control the order of actions in the submenu." + ], + "signature": [ + "(submenuId: string, appMenuItem: ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuSubmenuActionCustom", + "text": "AppMenuSubmenuActionCustom" + }, + " | ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuSubmenuHorizontalRule", + "text": "AppMenuSubmenuHorizontalRule" + }, + ") => void" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/app_menu_registry.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuRegistry.registerCustomActionUnderSubmenu.$1", + "type": "string", + "tags": [], + "label": "submenuId", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/app_menu_registry.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuRegistry.registerCustomActionUnderSubmenu.$2", + "type": "CompoundType", + "tags": [], + "label": "appMenuItem", + "description": [], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuSubmenuActionCustom", + "text": "AppMenuSubmenuActionCustom" + }, + " | ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuSubmenuHorizontalRule", + "text": "AppMenuSubmenuHorizontalRule" + } + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/app_menu_registry.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuRegistry.getSortedItems", + "type": "Function", + "tags": [], + "label": "getSortedItems", + "description": [ + "\nGet the resulting app menu items sorted by type and order." + ], + "signature": [ + "() => ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuItem", + "text": "AppMenuItem" + }, + "[]" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/app_menu_registry.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], "functions": [ { "parentPluginId": "@kbn/discover-utils", @@ -2038,81 +2297,37 @@ "interfaces": [ { "parentPluginId": "@kbn/discover-utils", - "id": "def-common.DataTableRecord", + "id": "def-common.AppMenuActionBase", "type": "Interface", "tags": [], - "label": "DataTableRecord", - "description": [ - "\nThis is the record/row of data provided to our Data Table" - ], - "path": "packages/kbn-discover-utils/src/types.ts", + "label": "AppMenuActionBase", + "description": [], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/discover-utils", - "id": "def-common.DataTableRecord.id", + "id": "def-common.AppMenuActionBase.id", "type": "string", "tags": [], "label": "id", - "description": [ - "\nA unique id generated by index, id and routing of a record" - ], - "path": "packages/kbn-discover-utils/src/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/discover-utils", - "id": "def-common.DataTableRecord.raw", - "type": "Object", - "tags": [], - "label": "raw", - "description": [ - "\nThe document returned by Elasticsearch for search queries" - ], - "signature": [ - { - "pluginId": "@kbn/discover-utils", - "scope": "common", - "docId": "kibKbnDiscoverUtilsPluginApi", - "section": "def-common.EsHitRecord", - "text": "EsHitRecord" - } - ], - "path": "packages/kbn-discover-utils/src/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/discover-utils", - "id": "def-common.DataTableRecord.flattened", - "type": "Object", - "tags": [], - "label": "flattened", - "description": [ - "\nA flattened version of the ES doc or data provided by SQL, aggregations ..." - ], - "signature": [ - "{ [x: string]: unknown; }" - ], - "path": "packages/kbn-discover-utils/src/types.ts", + "description": [], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/discover-utils", - "id": "def-common.DataTableRecord.isAnchor", - "type": "CompoundType", + "id": "def-common.AppMenuActionBase.order", + "type": "number", "tags": [], - "label": "isAnchor", - "description": [ - "\nDetermines that the given doc is the anchor doc when rendering view surrounding docs" - ], + "label": "order", + "description": [], "signature": [ - "boolean | undefined" + "number | undefined" ], - "path": "packages/kbn-discover-utils/src/types.ts", + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", "deprecated": false, "trackAdoption": false } @@ -2121,20 +2336,807 @@ }, { "parentPluginId": "@kbn/discover-utils", - "id": "def-common.EsHitRecord", + "id": "def-common.AppMenuActionCustom", "type": "Interface", "tags": [], - "label": "EsHitRecord", - "description": [], + "label": "AppMenuActionCustom", + "description": [ + "\nA custom menu action" + ], "signature": [ { "pluginId": "@kbn/discover-utils", "scope": "common", "docId": "kibKbnDiscoverUtilsPluginApi", - "section": "def-common.EsHitRecord", - "text": "EsHitRecord" + "section": "def-common.AppMenuActionCustom", + "text": "AppMenuActionCustom" }, - " extends Omit" + " extends ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionBase", + "text": "AppMenuActionBase" + } + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuActionCustom.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionType", + "text": "AppMenuActionType" + }, + ".custom" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuActionCustom.controlProps", + "type": "CompoundType", + "tags": [], + "label": "controlProps", + "description": [], + "signature": [ + "Pick<", + { + "pluginId": "navigation", + "scope": "public", + "docId": "kibNavigationPluginApi", + "section": "def-public.TopNavMenuData", + "text": "TopNavMenuData" + }, + ", \"isLoading\" | \"description\" | \"label\" | \"href\" | \"tooltip\" | \"testId\" | \"disableButton\"> & { onClick: ((params: ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuControlOnClickParams", + "text": "AppMenuControlOnClickParams" + }, + ") => Promise) | ((params: ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuControlOnClickParams", + "text": "AppMenuControlOnClickParams" + }, + ") => void | React.ReactNode) | undefined; }" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuActionPrimary", + "type": "Interface", + "tags": [], + "label": "AppMenuActionPrimary", + "description": [ + "\nA primary menu action (with icon only)" + ], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionPrimary", + "text": "AppMenuActionPrimary" + }, + " extends ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionBase", + "text": "AppMenuActionBase" + } + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuActionPrimary.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionType", + "text": "AppMenuActionType" + }, + ".primary" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuActionPrimary.controlProps", + "type": "CompoundType", + "tags": [], + "label": "controlProps", + "description": [], + "signature": [ + "Pick<", + { + "pluginId": "navigation", + "scope": "public", + "docId": "kibNavigationPluginApi", + "section": "def-public.TopNavMenuData", + "text": "TopNavMenuData" + }, + ", \"isLoading\" | \"description\" | \"label\" | \"href\" | \"tooltip\" | \"testId\" | \"disableButton\"> & { onClick: ((params: ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuControlOnClickParams", + "text": "AppMenuControlOnClickParams" + }, + ") => Promise) | ((params: ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuControlOnClickParams", + "text": "AppMenuControlOnClickParams" + }, + ") => void | React.ReactNode) | undefined; } & { iconType: \"string\" | \"number\" | \"function\" | \"key\" | \"namespace\" | \"error\" | \"filter\" | \"search\" | \"link\" | \"at\" | \"nested\" | \"ip\" | \"push\" | \"list\" | \"cluster\" | \"eql\" | \"index\" | \"unlink\" | \"alert\" | \"color\" | \"grid\" | \"aggregate\" | \"warning\" | \"annotation\" | \"memory\" | \"stop\" | \"stats\" | \"mobile\" | \"article\" | \"menu\" | \"image\" | \"download\" | \"document\" | \"email\" | \"copy\" | \"move\" | \"merge\" | \"partial\" | \"container\" | \"user\" | \"pause\" | \"share\" | \"home\" | \"spaces\" | \"package\" | \"tag\" | \"beta\" | \"users\" | \"brush\" | \"percent\" | \"temperature\" | \"accessibility\" | \"addDataApp\" | \"advancedSettingsApp\" | \"agentApp\" | \"analyzeEvent\" | \"anomalyChart\" | \"anomalySwimLane\" | \"apmApp\" | \"apmTrace\" | \"appSearchApp\" | \"apps\" | \"arrowDown\" | \"arrowLeft\" | \"arrowRight\" | \"arrowUp\" | \"arrowStart\" | \"arrowEnd\" | \"asterisk\" | \"auditbeatApp\" | \"beaker\" | \"bell\" | \"bellSlash\" | \"bolt\" | \"boxesHorizontal\" | \"boxesVertical\" | \"branch\" | \"branchUser\" | \"broom\" | \"bug\" | \"bullseye\" | \"calendar\" | \"canvasApp\" | \"casesApp\" | \"changePointDetection\" | \"check\" | \"checkInCircleFilled\" | \"cheer\" | \"classificationJob\" | \"clickLeft\" | \"clickRight\" | \"clock\" | \"clockCounter\" | \"cloudDrizzle\" | \"cloudStormy\" | \"cloudSunny\" | \"codeApp\" | \"compute\" | \"console\" | \"consoleApp\" | \"continuityAbove\" | \"continuityAboveBelow\" | \"continuityBelow\" | \"continuityWithin\" | \"controlsHorizontal\" | \"controlsVertical\" | \"copyClipboard\" | \"createAdvancedJob\" | \"createMultiMetricJob\" | \"createPopulationJob\" | \"createSingleMetricJob\" | \"cross\" | \"crossClusterReplicationApp\" | \"crossInCircle\" | \"crosshairs\" | \"currency\" | \"cut\" | \"dashboardApp\" | \"dataVisualizer\" | \"database\" | \"desktop\" | \"devToolsApp\" | \"diff\" | \"discoverApp\" | \"discuss\" | \"documentEdit\" | \"documentation\" | \"documents\" | \"dot\" | \"dotInCircle\" | \"doubleArrowLeft\" | \"doubleArrowRight\" | \"editorAlignCenter\" | \"editorAlignLeft\" | \"editorAlignRight\" | \"editorBold\" | \"editorChecklist\" | \"editorCodeBlock\" | \"editorComment\" | \"editorDistributeHorizontal\" | \"editorDistributeVertical\" | \"editorHeading\" | \"editorItalic\" | \"editorItemAlignBottom\" | \"editorItemAlignCenter\" | \"editorItemAlignLeft\" | \"editorItemAlignMiddle\" | \"editorItemAlignRight\" | \"editorItemAlignTop\" | \"editorLink\" | \"editorOrderedList\" | \"editorPositionBottomLeft\" | \"editorPositionBottomRight\" | \"editorPositionTopLeft\" | \"editorPositionTopRight\" | \"editorRedo\" | \"editorStrike\" | \"editorTable\" | \"editorUnderline\" | \"editorUndo\" | \"editorUnorderedList\" | \"empty\" | \"emsApp\" | \"endpoint\" | \"eraser\" | \"errorFilled\" | \"esqlVis\" | \"exit\" | \"expand\" | \"expandMini\" | \"exportAction\" | \"eye\" | \"eyeClosed\" | \"faceHappy\" | \"faceNeutral\" | \"faceSad\" | \"fieldStatistics\" | \"filebeatApp\" | \"filterExclude\" | \"filterIgnore\" | \"filterInclude\" | \"filterInCircle\" | \"flag\" | \"fleetApp\" | \"fold\" | \"folderCheck\" | \"folderClosed\" | \"folderExclamation\" | \"folderOpen\" | \"frameNext\" | \"framePrevious\" | \"fullScreen\" | \"fullScreenExit\" | \"gear\" | \"gisApp\" | \"glasses\" | \"globe\" | \"grab\" | \"grabHorizontal\" | \"grabOmnidirectional\" | \"gradient\" | \"graphApp\" | \"grokApp\" | \"heart\" | \"heartbeatApp\" | \"heatmap\" | \"help\" | \"iInCircle\" | \"importAction\" | \"indexClose\" | \"indexEdit\" | \"indexFlush\" | \"indexManagementApp\" | \"indexMapping\" | \"indexOpen\" | \"indexPatternApp\" | \"indexRollupApp\" | \"indexRuntime\" | \"indexSettings\" | \"indexTemporary\" | \"infinity\" | \"inputOutput\" | \"inspect\" | \"invert\" | \"keyboard\" | \"kqlField\" | \"kqlFunction\" | \"kqlOperand\" | \"kqlSelector\" | \"kqlValue\" | \"kubernetesNode\" | \"kubernetesPod\" | \"launch\" | \"layers\" | \"lensApp\" | \"lettering\" | \"lineDashed\" | \"lineDotted\" | \"lineSolid\" | \"listAdd\" | \"lock\" | \"lockOpen\" | \"logPatternAnalysis\" | \"logRateAnalysis\" | \"logoAWS\" | \"logoAWSMono\" | \"logoAerospike\" | \"logoApache\" | \"logoAppSearch\" | \"logoAzure\" | \"logoAzureMono\" | \"logoBeats\" | \"logoBusinessAnalytics\" | \"logoCeph\" | \"logoCloud\" | \"logoCloudEnterprise\" | \"logoCode\" | \"logoCodesandbox\" | \"logoCouchbase\" | \"logoDocker\" | \"logoDropwizard\" | \"logoElastic\" | \"logoElasticStack\" | \"logoElasticsearch\" | \"logoEnterpriseSearch\" | \"logoEtcd\" | \"logoGCP\" | \"logoGCPMono\" | \"logoGithub\" | \"logoGmail\" | \"logoGolang\" | \"logoGoogleG\" | \"logoHAproxy\" | \"logoIBM\" | \"logoIBMMono\" | \"logoKafka\" | \"logoKibana\" | \"logoKubernetes\" | \"logoLogging\" | \"logoLogstash\" | \"logoMaps\" | \"logoMemcached\" | \"logoMetrics\" | \"logoMongodb\" | \"logoMySQL\" | \"logoNginx\" | \"logoObservability\" | \"logoOsquery\" | \"logoPhp\" | \"logoPostgres\" | \"logoPrometheus\" | \"logoRabbitmq\" | \"logoRedis\" | \"logoSecurity\" | \"logoSiteSearch\" | \"logoSketch\" | \"logoSlack\" | \"logoUptime\" | \"logoVulnerabilityManagement\" | \"logoWebhook\" | \"logoWindows\" | \"logoWorkplaceSearch\" | \"logsApp\" | \"logstashFilter\" | \"logstashIf\" | \"logstashInput\" | \"logstashOutput\" | \"logstashQueue\" | \"machineLearningApp\" | \"magnet\" | \"magnifyWithExclamation\" | \"magnifyWithMinus\" | \"magnifyWithPlus\" | \"managementApp\" | \"mapMarker\" | \"menuDown\" | \"menuLeft\" | \"menuRight\" | \"menuUp\" | \"metricbeatApp\" | \"metricsApp\" | \"minimize\" | \"minus\" | \"minusInCircle\" | \"minusInCircleFilled\" | \"minusInSquare\" | \"monitoringApp\" | \"moon\" | \"newChat\" | \"node\" | \"notebookApp\" | \"offline\" | \"online\" | \"outlierDetectionJob\" | \"packetbeatApp\" | \"pageSelect\" | \"pagesSelect\" | \"palette\" | \"paperClip\" | \"payment\" | \"pencil\" | \"pin\" | \"pinFilled\" | \"pipeBreaks\" | \"pipelineApp\" | \"pipeNoBreaks\" | \"pivot\" | \"play\" | \"playFilled\" | \"plus\" | \"plusInCircle\" | \"plusInCircleFilled\" | \"plusInSquare\" | \"popout\" | \"questionInCircle\" | \"quote\" | \"recentlyViewedApp\" | \"refresh\" | \"regressionJob\" | \"reporter\" | \"reportingApp\" | \"returnKey\" | \"save\" | \"savedObjectsApp\" | \"scale\" | \"searchProfilerApp\" | \"securityAnalyticsApp\" | \"securityApp\" | \"securitySignal\" | \"securitySignalDetected\" | \"securitySignalResolved\" | \"sessionViewer\" | \"shard\" | \"singleMetricViewer\" | \"snowflake\" | \"sortAscending\" | \"sortDescending\" | \"sortDown\" | \"sortLeft\" | \"sortRight\" | \"sortUp\" | \"sortable\" | \"spacesApp\" | \"sparkles\" | \"sqlApp\" | \"starEmpty\" | \"starEmptySpace\" | \"starFilled\" | \"starFilledSpace\" | \"starMinusEmpty\" | \"starMinusFilled\" | \"starPlusEmpty\" | \"starPlusFilled\" | \"stopFilled\" | \"stopSlash\" | \"storage\" | \"submodule\" | \"sun\" | \"swatchInput\" | \"symlink\" | \"tableDensityCompact\" | \"tableDensityExpanded\" | \"tableDensityNormal\" | \"tableOfContents\" | \"tear\" | \"timeline\" | \"timelineWithArrow\" | \"timelionApp\" | \"timeRefresh\" | \"timeslider\" | \"training\" | \"transitionLeftIn\" | \"transitionLeftOut\" | \"transitionTopIn\" | \"transitionTopOut\" | \"trash\" | \"unfold\" | \"upgradeAssistantApp\" | \"uptimeApp\" | \"userAvatar\" | \"usersRolesApp\" | \"vector\" | \"videoPlayer\" | \"visArea\" | \"visAreaStacked\" | \"visBarHorizontal\" | \"visBarHorizontalStacked\" | \"visBarVertical\" | \"visBarVerticalStacked\" | \"visGauge\" | \"visGoal\" | \"visLine\" | \"visMapCoordinate\" | \"visMapRegion\" | \"visMetric\" | \"visPie\" | \"visTable\" | \"visTagCloud\" | \"visText\" | \"visTimelion\" | \"visVega\" | \"visVisualBuilder\" | \"visualizeApp\" | \"vulnerabilityManagementApp\" | \"warningFilled\" | \"watchesApp\" | \"wordWrap\" | \"wordWrapDisabled\" | \"workplaceSearchApp\" | \"wrench\" | \"tokenAlias\" | \"tokenAnnotation\" | \"tokenArray\" | \"tokenBinary\" | \"tokenBoolean\" | \"tokenClass\" | \"tokenCompletionSuggester\" | \"tokenConstant\" | \"tokenDate\" | \"tokenDimension\" | \"tokenElement\" | \"tokenEnum\" | \"tokenEnumMember\" | \"tokenEvent\" | \"tokenException\" | \"tokenField\" | \"tokenFile\" | \"tokenFlattened\" | \"tokenFunction\" | \"tokenGeo\" | \"tokenHistogram\" | \"tokenInterface\" | \"tokenIP\" | \"tokenJoin\" | \"tokenKey\" | \"tokenKeyword\" | \"tokenMethod\" | \"tokenMetricCounter\" | \"tokenMetricGauge\" | \"tokenModule\" | \"tokenNamespace\" | \"tokenNested\" | \"tokenNull\" | \"tokenNumber\" | \"tokenObject\" | \"tokenOperator\" | \"tokenPackage\" | \"tokenParameter\" | \"tokenPercolator\" | \"tokenProperty\" | \"tokenRange\" | \"tokenRankFeature\" | \"tokenRankFeatures\" | \"tokenRepo\" | \"tokenSearchType\" | \"tokenSemanticText\" | \"tokenShape\" | \"tokenString\" | \"tokenStruct\" | \"tokenSymbol\" | \"tokenTag\" | \"tokenText\" | \"tokenTokenCount\" | \"tokenVariable\" | \"tokenVectorDense\" | \"tokenDenseVector\" | \"tokenVectorSparse\"; }" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuActionSecondary", + "type": "Interface", + "tags": [], + "label": "AppMenuActionSecondary", + "description": [ + "\nA secondary menu action" + ], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionSecondary", + "text": "AppMenuActionSecondary" + }, + " extends ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionBase", + "text": "AppMenuActionBase" + } + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuActionSecondary.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionType", + "text": "AppMenuActionType" + }, + ".secondary" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuActionSecondary.controlProps", + "type": "CompoundType", + "tags": [], + "label": "controlProps", + "description": [], + "signature": [ + "Pick<", + { + "pluginId": "navigation", + "scope": "public", + "docId": "kibNavigationPluginApi", + "section": "def-public.TopNavMenuData", + "text": "TopNavMenuData" + }, + ", \"isLoading\" | \"description\" | \"label\" | \"href\" | \"tooltip\" | \"testId\" | \"disableButton\"> & { onClick: ((params: ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuControlOnClickParams", + "text": "AppMenuControlOnClickParams" + }, + ") => Promise) | ((params: ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuControlOnClickParams", + "text": "AppMenuControlOnClickParams" + }, + ") => void | React.ReactNode) | undefined; }" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuActionSubmenuBase", + "type": "Interface", + "tags": [], + "label": "AppMenuActionSubmenuBase", + "description": [ + "\nA menu action which opens a submenu with more actions" + ], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionSubmenuBase", + "text": "AppMenuActionSubmenuBase" + }, + " extends ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionBase", + "text": "AppMenuActionBase" + } + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuActionSubmenuBase.type", + "type": "Uncategorized", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "T extends ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionSecondary", + "text": "AppMenuActionSecondary" + }, + " ? ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionType", + "text": "AppMenuActionType" + }, + ".secondary : ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionType", + "text": "AppMenuActionType" + }, + ".custom" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuActionSubmenuBase.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuActionSubmenuBase.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuActionSubmenuBase.testId", + "type": "string", + "tags": [], + "label": "testId", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuActionSubmenuBase.actions", + "type": "Uncategorized", + "tags": [], + "label": "actions", + "description": [], + "signature": [ + "T extends ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionSecondary", + "text": "AppMenuActionSecondary" + }, + " ? (", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuSubmenuActionSecondary", + "text": "AppMenuSubmenuActionSecondary" + }, + " | ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuSubmenuActionCustom", + "text": "AppMenuSubmenuActionCustom" + }, + " | ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuSubmenuHorizontalRule", + "text": "AppMenuSubmenuHorizontalRule" + }, + ")[] : (", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuSubmenuActionCustom", + "text": "AppMenuSubmenuActionCustom" + }, + " | ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuSubmenuHorizontalRule", + "text": "AppMenuSubmenuHorizontalRule" + }, + ")[]" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuControlOnClickParams", + "type": "Interface", + "tags": [], + "label": "AppMenuControlOnClickParams", + "description": [], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuControlOnClickParams.anchorElement", + "type": "Object", + "tags": [], + "label": "anchorElement", + "description": [], + "signature": [ + "HTMLElement" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuControlOnClickParams.onFinishAction", + "type": "Function", + "tags": [], + "label": "onFinishAction", + "description": [], + "signature": [ + "() => void" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuSubmenuActionCustom", + "type": "Interface", + "tags": [], + "label": "AppMenuSubmenuActionCustom", + "description": [ + "\nA custom submenu action" + ], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuSubmenuActionCustom", + "text": "AppMenuSubmenuActionCustom" + }, + " extends Omit<", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionCustom", + "text": "AppMenuActionCustom" + }, + ", \"controlProps\">" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuSubmenuActionCustom.controlProps", + "type": "CompoundType", + "tags": [], + "label": "controlProps", + "description": [], + "signature": [ + "Pick<", + { + "pluginId": "navigation", + "scope": "public", + "docId": "kibNavigationPluginApi", + "section": "def-public.TopNavMenuData", + "text": "TopNavMenuData" + }, + ", \"isLoading\" | \"description\" | \"label\" | \"href\" | \"tooltip\" | \"testId\" | \"disableButton\"> & { onClick: ((params: ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuControlOnClickParams", + "text": "AppMenuControlOnClickParams" + }, + ") => Promise) | ((params: ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuControlOnClickParams", + "text": "AppMenuControlOnClickParams" + }, + ") => void | React.ReactNode) | undefined; } & ControlWithOptionalIcon" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuSubmenuActionSecondary", + "type": "Interface", + "tags": [], + "label": "AppMenuSubmenuActionSecondary", + "description": [ + "\nA secondary submenu action" + ], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuSubmenuActionSecondary", + "text": "AppMenuSubmenuActionSecondary" + }, + " extends Omit<", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionSecondary", + "text": "AppMenuActionSecondary" + }, + ", \"controlProps\">" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuSubmenuActionSecondary.controlProps", + "type": "CompoundType", + "tags": [], + "label": "controlProps", + "description": [], + "signature": [ + "Pick<", + { + "pluginId": "navigation", + "scope": "public", + "docId": "kibNavigationPluginApi", + "section": "def-public.TopNavMenuData", + "text": "TopNavMenuData" + }, + ", \"isLoading\" | \"description\" | \"label\" | \"href\" | \"tooltip\" | \"testId\" | \"disableButton\"> & { onClick: ((params: ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuControlOnClickParams", + "text": "AppMenuControlOnClickParams" + }, + ") => Promise) | ((params: ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuControlOnClickParams", + "text": "AppMenuControlOnClickParams" + }, + ") => void | React.ReactNode) | undefined; } & ControlWithOptionalIcon" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuSubmenuHorizontalRule", + "type": "Interface", + "tags": [], + "label": "AppMenuSubmenuHorizontalRule", + "description": [ + "\nA horizontal rule between menu items" + ], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuSubmenuHorizontalRule", + "text": "AppMenuSubmenuHorizontalRule" + }, + " extends ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionBase", + "text": "AppMenuActionBase" + } + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuSubmenuHorizontalRule.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionType", + "text": "AppMenuActionType" + }, + ".submenuHorizontalRule" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuSubmenuHorizontalRule.testId", + "type": "string", + "tags": [], + "label": "testId", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.DataTableRecord", + "type": "Interface", + "tags": [], + "label": "DataTableRecord", + "description": [ + "\nThis is the record/row of data provided to our Data Table" + ], + "path": "packages/kbn-discover-utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.DataTableRecord.id", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "\nA unique id generated by index, id and routing of a record" + ], + "path": "packages/kbn-discover-utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.DataTableRecord.raw", + "type": "Object", + "tags": [], + "label": "raw", + "description": [ + "\nThe document returned by Elasticsearch for search queries" + ], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.EsHitRecord", + "text": "EsHitRecord" + } + ], + "path": "packages/kbn-discover-utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.DataTableRecord.flattened", + "type": "Object", + "tags": [], + "label": "flattened", + "description": [ + "\nA flattened version of the ES doc or data provided by SQL, aggregations ..." + ], + "signature": [ + "{ [x: string]: unknown; }" + ], + "path": "packages/kbn-discover-utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.DataTableRecord.isAnchor", + "type": "CompoundType", + "tags": [], + "label": "isAnchor", + "description": [ + "\nDetermines that the given doc is the anchor doc when rendering view surrounding docs" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-discover-utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.EsHitRecord", + "type": "Interface", + "tags": [], + "label": "EsHitRecord", + "description": [], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.EsHitRecord", + "text": "EsHitRecord" + }, + " extends Omit" ], "path": "packages/kbn-discover-utils/src/types.ts", "deprecated": false, @@ -2987,6 +3989,30 @@ } ], "enums": [ + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuActionId", + "type": "Enum", + "tags": [], + "label": "AppMenuActionId", + "description": [], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuActionType", + "type": "Enum", + "tags": [], + "label": "AppMenuActionType", + "description": [], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/discover-utils", "id": "def-common.DiscoverFlyouts", @@ -3055,6 +4081,272 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuActionSubmenuCustom", + "type": "Type", + "tags": [], + "label": "AppMenuActionSubmenuCustom", + "description": [ + "\nA menu action which opens a submenu with more custom actions" + ], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionSubmenuBase", + "text": "AppMenuActionSubmenuBase" + }, + "<", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionCustom", + "text": "AppMenuActionCustom" + }, + ">" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuActionSubmenuSecondary", + "type": "Type", + "tags": [], + "label": "AppMenuActionSubmenuSecondary", + "description": [ + "\nA menu action which opens a submenu with more secondary actions" + ], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionSubmenuBase", + "text": "AppMenuActionSubmenuBase" + }, + "<", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionSecondary", + "text": "AppMenuActionSecondary" + }, + ">" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuControlProps", + "type": "Type", + "tags": [], + "label": "AppMenuControlProps", + "description": [], + "signature": [ + "Pick<", + { + "pluginId": "navigation", + "scope": "public", + "docId": "kibNavigationPluginApi", + "section": "def-public.TopNavMenuData", + "text": "TopNavMenuData" + }, + ", \"isLoading\" | \"description\" | \"label\" | \"href\" | \"tooltip\" | \"testId\" | \"disableButton\"> & { onClick: ((params: ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuControlOnClickParams", + "text": "AppMenuControlOnClickParams" + }, + ") => Promise) | ((params: ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuControlOnClickParams", + "text": "AppMenuControlOnClickParams" + }, + ") => void | React.ReactNode) | undefined; }" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuControlWithIconProps", + "type": "Type", + "tags": [], + "label": "AppMenuControlWithIconProps", + "description": [], + "signature": [ + "Pick<", + { + "pluginId": "navigation", + "scope": "public", + "docId": "kibNavigationPluginApi", + "section": "def-public.TopNavMenuData", + "text": "TopNavMenuData" + }, + ", \"isLoading\" | \"description\" | \"label\" | \"href\" | \"tooltip\" | \"testId\" | \"disableButton\"> & { onClick: ((params: ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuControlOnClickParams", + "text": "AppMenuControlOnClickParams" + }, + ") => Promise) | ((params: ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuControlOnClickParams", + "text": "AppMenuControlOnClickParams" + }, + ") => void | React.ReactNode) | undefined; } & { iconType: \"string\" | \"number\" | \"function\" | \"key\" | \"namespace\" | \"error\" | \"filter\" | \"search\" | \"link\" | \"at\" | \"nested\" | \"ip\" | \"push\" | \"list\" | \"cluster\" | \"eql\" | \"index\" | \"unlink\" | \"alert\" | \"color\" | \"grid\" | \"aggregate\" | \"warning\" | \"annotation\" | \"memory\" | \"stop\" | \"stats\" | \"mobile\" | \"article\" | \"menu\" | \"image\" | \"download\" | \"document\" | \"email\" | \"copy\" | \"move\" | \"merge\" | \"partial\" | \"container\" | \"user\" | \"pause\" | \"share\" | \"home\" | \"spaces\" | \"package\" | \"tag\" | \"beta\" | \"users\" | \"brush\" | \"percent\" | \"temperature\" | \"accessibility\" | \"addDataApp\" | \"advancedSettingsApp\" | \"agentApp\" | \"analyzeEvent\" | \"anomalyChart\" | \"anomalySwimLane\" | \"apmApp\" | \"apmTrace\" | \"appSearchApp\" | \"apps\" | \"arrowDown\" | \"arrowLeft\" | \"arrowRight\" | \"arrowUp\" | \"arrowStart\" | \"arrowEnd\" | \"asterisk\" | \"auditbeatApp\" | \"beaker\" | \"bell\" | \"bellSlash\" | \"bolt\" | \"boxesHorizontal\" | \"boxesVertical\" | \"branch\" | \"branchUser\" | \"broom\" | \"bug\" | \"bullseye\" | \"calendar\" | \"canvasApp\" | \"casesApp\" | \"changePointDetection\" | \"check\" | \"checkInCircleFilled\" | \"cheer\" | \"classificationJob\" | \"clickLeft\" | \"clickRight\" | \"clock\" | \"clockCounter\" | \"cloudDrizzle\" | \"cloudStormy\" | \"cloudSunny\" | \"codeApp\" | \"compute\" | \"console\" | \"consoleApp\" | \"continuityAbove\" | \"continuityAboveBelow\" | \"continuityBelow\" | \"continuityWithin\" | \"controlsHorizontal\" | \"controlsVertical\" | \"copyClipboard\" | \"createAdvancedJob\" | \"createMultiMetricJob\" | \"createPopulationJob\" | \"createSingleMetricJob\" | \"cross\" | \"crossClusterReplicationApp\" | \"crossInCircle\" | \"crosshairs\" | \"currency\" | \"cut\" | \"dashboardApp\" | \"dataVisualizer\" | \"database\" | \"desktop\" | \"devToolsApp\" | \"diff\" | \"discoverApp\" | \"discuss\" | \"documentEdit\" | \"documentation\" | \"documents\" | \"dot\" | \"dotInCircle\" | \"doubleArrowLeft\" | \"doubleArrowRight\" | \"editorAlignCenter\" | \"editorAlignLeft\" | \"editorAlignRight\" | \"editorBold\" | \"editorChecklist\" | \"editorCodeBlock\" | \"editorComment\" | \"editorDistributeHorizontal\" | \"editorDistributeVertical\" | \"editorHeading\" | \"editorItalic\" | \"editorItemAlignBottom\" | \"editorItemAlignCenter\" | \"editorItemAlignLeft\" | \"editorItemAlignMiddle\" | \"editorItemAlignRight\" | \"editorItemAlignTop\" | \"editorLink\" | \"editorOrderedList\" | \"editorPositionBottomLeft\" | \"editorPositionBottomRight\" | \"editorPositionTopLeft\" | \"editorPositionTopRight\" | \"editorRedo\" | \"editorStrike\" | \"editorTable\" | \"editorUnderline\" | \"editorUndo\" | \"editorUnorderedList\" | \"empty\" | \"emsApp\" | \"endpoint\" | \"eraser\" | \"errorFilled\" | \"esqlVis\" | \"exit\" | \"expand\" | \"expandMini\" | \"exportAction\" | \"eye\" | \"eyeClosed\" | \"faceHappy\" | \"faceNeutral\" | \"faceSad\" | \"fieldStatistics\" | \"filebeatApp\" | \"filterExclude\" | \"filterIgnore\" | \"filterInclude\" | \"filterInCircle\" | \"flag\" | \"fleetApp\" | \"fold\" | \"folderCheck\" | \"folderClosed\" | \"folderExclamation\" | \"folderOpen\" | \"frameNext\" | \"framePrevious\" | \"fullScreen\" | \"fullScreenExit\" | \"gear\" | \"gisApp\" | \"glasses\" | \"globe\" | \"grab\" | \"grabHorizontal\" | \"grabOmnidirectional\" | \"gradient\" | \"graphApp\" | \"grokApp\" | \"heart\" | \"heartbeatApp\" | \"heatmap\" | \"help\" | \"iInCircle\" | \"importAction\" | \"indexClose\" | \"indexEdit\" | \"indexFlush\" | \"indexManagementApp\" | \"indexMapping\" | \"indexOpen\" | \"indexPatternApp\" | \"indexRollupApp\" | \"indexRuntime\" | \"indexSettings\" | \"indexTemporary\" | \"infinity\" | \"inputOutput\" | \"inspect\" | \"invert\" | \"keyboard\" | \"kqlField\" | \"kqlFunction\" | \"kqlOperand\" | \"kqlSelector\" | \"kqlValue\" | \"kubernetesNode\" | \"kubernetesPod\" | \"launch\" | \"layers\" | \"lensApp\" | \"lettering\" | \"lineDashed\" | \"lineDotted\" | \"lineSolid\" | \"listAdd\" | \"lock\" | \"lockOpen\" | \"logPatternAnalysis\" | \"logRateAnalysis\" | \"logoAWS\" | \"logoAWSMono\" | \"logoAerospike\" | \"logoApache\" | \"logoAppSearch\" | \"logoAzure\" | \"logoAzureMono\" | \"logoBeats\" | \"logoBusinessAnalytics\" | \"logoCeph\" | \"logoCloud\" | \"logoCloudEnterprise\" | \"logoCode\" | \"logoCodesandbox\" | \"logoCouchbase\" | \"logoDocker\" | \"logoDropwizard\" | \"logoElastic\" | \"logoElasticStack\" | \"logoElasticsearch\" | \"logoEnterpriseSearch\" | \"logoEtcd\" | \"logoGCP\" | \"logoGCPMono\" | \"logoGithub\" | \"logoGmail\" | \"logoGolang\" | \"logoGoogleG\" | \"logoHAproxy\" | \"logoIBM\" | \"logoIBMMono\" | \"logoKafka\" | \"logoKibana\" | \"logoKubernetes\" | \"logoLogging\" | \"logoLogstash\" | \"logoMaps\" | \"logoMemcached\" | \"logoMetrics\" | \"logoMongodb\" | \"logoMySQL\" | \"logoNginx\" | \"logoObservability\" | \"logoOsquery\" | \"logoPhp\" | \"logoPostgres\" | \"logoPrometheus\" | \"logoRabbitmq\" | \"logoRedis\" | \"logoSecurity\" | \"logoSiteSearch\" | \"logoSketch\" | \"logoSlack\" | \"logoUptime\" | \"logoVulnerabilityManagement\" | \"logoWebhook\" | \"logoWindows\" | \"logoWorkplaceSearch\" | \"logsApp\" | \"logstashFilter\" | \"logstashIf\" | \"logstashInput\" | \"logstashOutput\" | \"logstashQueue\" | \"machineLearningApp\" | \"magnet\" | \"magnifyWithExclamation\" | \"magnifyWithMinus\" | \"magnifyWithPlus\" | \"managementApp\" | \"mapMarker\" | \"menuDown\" | \"menuLeft\" | \"menuRight\" | \"menuUp\" | \"metricbeatApp\" | \"metricsApp\" | \"minimize\" | \"minus\" | \"minusInCircle\" | \"minusInCircleFilled\" | \"minusInSquare\" | \"monitoringApp\" | \"moon\" | \"newChat\" | \"node\" | \"notebookApp\" | \"offline\" | \"online\" | \"outlierDetectionJob\" | \"packetbeatApp\" | \"pageSelect\" | \"pagesSelect\" | \"palette\" | \"paperClip\" | \"payment\" | \"pencil\" | \"pin\" | \"pinFilled\" | \"pipeBreaks\" | \"pipelineApp\" | \"pipeNoBreaks\" | \"pivot\" | \"play\" | \"playFilled\" | \"plus\" | \"plusInCircle\" | \"plusInCircleFilled\" | \"plusInSquare\" | \"popout\" | \"questionInCircle\" | \"quote\" | \"recentlyViewedApp\" | \"refresh\" | \"regressionJob\" | \"reporter\" | \"reportingApp\" | \"returnKey\" | \"save\" | \"savedObjectsApp\" | \"scale\" | \"searchProfilerApp\" | \"securityAnalyticsApp\" | \"securityApp\" | \"securitySignal\" | \"securitySignalDetected\" | \"securitySignalResolved\" | \"sessionViewer\" | \"shard\" | \"singleMetricViewer\" | \"snowflake\" | \"sortAscending\" | \"sortDescending\" | \"sortDown\" | \"sortLeft\" | \"sortRight\" | \"sortUp\" | \"sortable\" | \"spacesApp\" | \"sparkles\" | \"sqlApp\" | \"starEmpty\" | \"starEmptySpace\" | \"starFilled\" | \"starFilledSpace\" | \"starMinusEmpty\" | \"starMinusFilled\" | \"starPlusEmpty\" | \"starPlusFilled\" | \"stopFilled\" | \"stopSlash\" | \"storage\" | \"submodule\" | \"sun\" | \"swatchInput\" | \"symlink\" | \"tableDensityCompact\" | \"tableDensityExpanded\" | \"tableDensityNormal\" | \"tableOfContents\" | \"tear\" | \"timeline\" | \"timelineWithArrow\" | \"timelionApp\" | \"timeRefresh\" | \"timeslider\" | \"training\" | \"transitionLeftIn\" | \"transitionLeftOut\" | \"transitionTopIn\" | \"transitionTopOut\" | \"trash\" | \"unfold\" | \"upgradeAssistantApp\" | \"uptimeApp\" | \"userAvatar\" | \"usersRolesApp\" | \"vector\" | \"videoPlayer\" | \"visArea\" | \"visAreaStacked\" | \"visBarHorizontal\" | \"visBarHorizontalStacked\" | \"visBarVertical\" | \"visBarVerticalStacked\" | \"visGauge\" | \"visGoal\" | \"visLine\" | \"visMapCoordinate\" | \"visMapRegion\" | \"visMetric\" | \"visPie\" | \"visTable\" | \"visTagCloud\" | \"visText\" | \"visTimelion\" | \"visVega\" | \"visVisualBuilder\" | \"visualizeApp\" | \"vulnerabilityManagementApp\" | \"warningFilled\" | \"watchesApp\" | \"wordWrap\" | \"wordWrapDisabled\" | \"workplaceSearchApp\" | \"wrench\" | \"tokenAlias\" | \"tokenAnnotation\" | \"tokenArray\" | \"tokenBinary\" | \"tokenBoolean\" | \"tokenClass\" | \"tokenCompletionSuggester\" | \"tokenConstant\" | \"tokenDate\" | \"tokenDimension\" | \"tokenElement\" | \"tokenEnum\" | \"tokenEnumMember\" | \"tokenEvent\" | \"tokenException\" | \"tokenField\" | \"tokenFile\" | \"tokenFlattened\" | \"tokenFunction\" | \"tokenGeo\" | \"tokenHistogram\" | \"tokenInterface\" | \"tokenIP\" | \"tokenJoin\" | \"tokenKey\" | \"tokenKeyword\" | \"tokenMethod\" | \"tokenMetricCounter\" | \"tokenMetricGauge\" | \"tokenModule\" | \"tokenNamespace\" | \"tokenNested\" | \"tokenNull\" | \"tokenNumber\" | \"tokenObject\" | \"tokenOperator\" | \"tokenPackage\" | \"tokenParameter\" | \"tokenPercolator\" | \"tokenProperty\" | \"tokenRange\" | \"tokenRankFeature\" | \"tokenRankFeatures\" | \"tokenRepo\" | \"tokenSearchType\" | \"tokenSemanticText\" | \"tokenShape\" | \"tokenString\" | \"tokenStruct\" | \"tokenSymbol\" | \"tokenTag\" | \"tokenText\" | \"tokenTokenCount\" | \"tokenVariable\" | \"tokenVectorDense\" | \"tokenDenseVector\" | \"tokenVectorSparse\"; }" + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuItem", + "type": "Type", + "tags": [], + "label": "AppMenuItem", + "description": [ + "\nA menu item can be primary, secondary or custom" + ], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionPrimary", + "text": "AppMenuActionPrimary" + }, + " | ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuItemSecondary", + "text": "AppMenuItemSecondary" + }, + " | ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuItemCustom", + "text": "AppMenuItemCustom" + } + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuItemCustom", + "type": "Type", + "tags": [], + "label": "AppMenuItemCustom", + "description": [ + "\nA custom menu item can have only a label or a submenu" + ], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionCustom", + "text": "AppMenuActionCustom" + }, + " | ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionSubmenuCustom", + "text": "AppMenuActionSubmenuCustom" + } + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuItemPrimary", + "type": "Type", + "tags": [], + "label": "AppMenuItemPrimary", + "description": [ + "\nA primary menu item can only have an icon" + ], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionPrimary", + "text": "AppMenuActionPrimary" + } + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/discover-utils", + "id": "def-common.AppMenuItemSecondary", + "type": "Type", + "tags": [], + "label": "AppMenuItemSecondary", + "description": [ + "\nA secondary menu item can have only a label or a submenu" + ], + "signature": [ + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionSecondary", + "text": "AppMenuActionSecondary" + }, + " | ", + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.AppMenuActionSubmenuSecondary", + "text": "AppMenuActionSubmenuSecondary" + } + ], + "path": "packages/kbn-discover-utils/src/components/app_menu/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/discover-utils", "id": "def-common.CLOUD_AVAILABILITY_ZONE_FIELD", diff --git a/api_docs/kbn_discover_utils.mdx b/api_docs/kbn_discover_utils.mdx index a0c5b677cfc2e..077389bd3e85d 100644 --- a/api_docs/kbn_discover_utils.mdx +++ b/api_docs/kbn_discover_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-discover-utils title: "@kbn/discover-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/discover-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/discover-utils'] --- import kbnDiscoverUtilsObj from './kbn_discover_utils.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 234 | 0 | 200 | 4 | +| 284 | 0 | 234 | 4 | ## Common @@ -31,6 +31,9 @@ Contact [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/k ### Functions +### Classes + + ### Interfaces diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 9551adaee19a2..dd5b3fd15c356 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2024-10-29 +date: 2024-11-05 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 af6b93c9f8e85..3403895e4f557 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2024-10-29 +date: 2024-11-05 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 53d508ae5425e..ccfcb07db1a67 100644 --- a/api_docs/kbn_dom_drag_drop.mdx +++ b/api_docs/kbn_dom_drag_drop.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dom-drag-drop title: "@kbn/dom-drag-drop" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dom-drag-drop plugin -date: 2024-10-29 +date: 2024-11-05 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 96aadc41ed447..49c9e7174ea51 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index d61268515c588..bdba3a8a40552 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_elastic_agent_utils.mdx b/api_docs/kbn_elastic_agent_utils.mdx index 35132a72ca72e..344a50712f427 100644 --- a/api_docs/kbn_elastic_agent_utils.mdx +++ b/api_docs/kbn_elastic_agent_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-agent-utils title: "@kbn/elastic-agent-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-agent-utils plugin -date: 2024-10-29 +date: 2024-11-05 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.mdx b/api_docs/kbn_elastic_assistant.mdx index 1b74cc3d2fb54..a798bf55dfcbd 100644 --- a/api_docs/kbn_elastic_assistant.mdx +++ b/api_docs/kbn_elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant title: "@kbn/elastic-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant'] --- import kbnElasticAssistantObj from './kbn_elastic_assistant.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant_common.devdocs.json b/api_docs/kbn_elastic_assistant_common.devdocs.json index d901a9c32b414..49dbaeea7049b 100644 --- a/api_docs/kbn_elastic_assistant_common.devdocs.json +++ b/api_docs/kbn_elastic_assistant_common.devdocs.json @@ -3613,7 +3613,7 @@ "label": "ReadKnowledgeBaseResponse", "description": [], "signature": [ - "{ elser_exists?: boolean | undefined; index_exists?: boolean | undefined; is_setup_available?: boolean | undefined; is_setup_in_progress?: boolean | undefined; pipeline_exists?: boolean | undefined; security_labs_exists?: boolean | undefined; }" + "{ elser_exists?: boolean | undefined; index_exists?: boolean | undefined; is_setup_available?: boolean | undefined; is_setup_in_progress?: boolean | undefined; pipeline_exists?: boolean | undefined; security_labs_exists?: boolean | undefined; user_data_exists?: boolean | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/knowledge_base/crud_kb_route.gen.ts", "deprecated": false, @@ -4737,7 +4737,7 @@ "\nDefault features available to the elastic assistant" ], "signature": [ - "{ readonly assistantKnowledgeBaseByDefault: false; readonly assistantModelEvaluation: false; }" + "{ readonly assistantKnowledgeBaseByDefault: true; readonly assistantModelEvaluation: false; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/capabilities/index.ts", "deprecated": false, @@ -5907,7 +5907,7 @@ "label": "ReadKnowledgeBaseResponse", "description": [], "signature": [ - "Zod.ZodObject<{ elser_exists: Zod.ZodOptional; index_exists: Zod.ZodOptional; is_setup_available: Zod.ZodOptional; is_setup_in_progress: Zod.ZodOptional; pipeline_exists: Zod.ZodOptional; security_labs_exists: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { elser_exists?: boolean | undefined; index_exists?: boolean | undefined; is_setup_available?: boolean | undefined; is_setup_in_progress?: boolean | undefined; pipeline_exists?: boolean | undefined; security_labs_exists?: boolean | undefined; }, { elser_exists?: boolean | undefined; index_exists?: boolean | undefined; is_setup_available?: boolean | undefined; is_setup_in_progress?: boolean | undefined; pipeline_exists?: boolean | undefined; security_labs_exists?: boolean | undefined; }>" + "Zod.ZodObject<{ elser_exists: Zod.ZodOptional; index_exists: Zod.ZodOptional; is_setup_available: Zod.ZodOptional; is_setup_in_progress: Zod.ZodOptional; pipeline_exists: Zod.ZodOptional; security_labs_exists: Zod.ZodOptional; user_data_exists: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { elser_exists?: boolean | undefined; index_exists?: boolean | undefined; is_setup_available?: boolean | undefined; is_setup_in_progress?: boolean | undefined; pipeline_exists?: boolean | undefined; security_labs_exists?: boolean | undefined; user_data_exists?: boolean | undefined; }, { elser_exists?: boolean | undefined; index_exists?: boolean | undefined; is_setup_available?: boolean | undefined; is_setup_in_progress?: boolean | undefined; pipeline_exists?: boolean | undefined; security_labs_exists?: boolean | undefined; user_data_exists?: boolean | undefined; }>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/knowledge_base/crud_kb_route.gen.ts", "deprecated": false, diff --git a/api_docs/kbn_elastic_assistant_common.mdx b/api_docs/kbn_elastic_assistant_common.mdx index 659bf1b27b071..7dd53c9a6e12f 100644 --- a/api_docs/kbn_elastic_assistant_common.mdx +++ b/api_docs/kbn_elastic_assistant_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant-common title: "@kbn/elastic-assistant-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant-common plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant-common'] --- import kbnElasticAssistantCommonObj from './kbn_elastic_assistant_common.devdocs.json'; diff --git a/api_docs/kbn_entities_schema.mdx b/api_docs/kbn_entities_schema.mdx index 9835e9b7335f8..38e018b197d02 100644 --- a/api_docs/kbn_entities_schema.mdx +++ b/api_docs/kbn_entities_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-entities-schema title: "@kbn/entities-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/entities-schema plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/entities-schema'] --- import kbnEntitiesSchemaObj from './kbn_entities_schema.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index 7dc8dba10337a..64c88a31a9141 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2024-10-29 +date: 2024-11-05 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 ef5072a347915..4fd0bddee77dc 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2024-10-29 +date: 2024-11-05 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 93e83620ef325..1215a2044ab96 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 9e1557f2cb9fb..77243315d5502 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; diff --git a/api_docs/kbn_es_types.devdocs.json b/api_docs/kbn_es_types.devdocs.json index 7505d6c6d2887..d9baac147e267 100644 --- a/api_docs/kbn_es_types.devdocs.json +++ b/api_docs/kbn_es_types.devdocs.json @@ -238,6 +238,21 @@ "path": "packages/kbn-es-types/src/search.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/es-types", + "id": "def-common.ESQLSearchResponse._clusters", + "type": "Object", + "tags": [], + "label": "_clusters", + "description": [], + "signature": [ + "ClusterStatistics", + " | undefined" + ], + "path": "packages/kbn-es-types/src/search.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index 6ca0de980b7fd..10da4a8198033 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-types'] --- import kbnEsTypesObj from './kbn_es_types.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 | |-------------------|-----------|------------------------|-----------------| -| 29 | 0 | 29 | 1 | +| 30 | 0 | 30 | 1 | ## Common diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index e831d2c8e0292..75e22b9832954 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_esql_ast.devdocs.json b/api_docs/kbn_esql_ast.devdocs.json index cac01fac9555d..94ef986f9809c 100644 --- a/api_docs/kbn_esql_ast.devdocs.json +++ b/api_docs/kbn_esql_ast.devdocs.json @@ -17949,6 +17949,31 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.ESQLFunction.operator", + "type": "CompoundType", + "tags": [], + "label": "operator", + "description": [ + "\nA node representing the function or operator being called." + ], + "signature": [ + { + "pluginId": "@kbn/esql-ast", + "scope": "common", + "docId": "kibKbnEsqlAstPluginApi", + "section": "def-common.ESQLParamLiteral", + "text": "ESQLParamLiteral" + }, + " | ", + "ESQLIdentifier", + " | undefined" + ], + "path": "packages/kbn-esql-ast/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/esql-ast", "id": "def-common.ESQLFunction.args", @@ -19533,6 +19558,8 @@ "text": "ESQLParamLiteral" }, " | ", + "ESQLIdentifier", + " | ", { "pluginId": "@kbn/esql-ast", "scope": "common", @@ -19668,6 +19695,8 @@ "text": "ESQLLiteral" }, " | ", + "ESQLIdentifier", + " | ", { "pluginId": "@kbn/esql-ast", "scope": "common", diff --git a/api_docs/kbn_esql_ast.mdx b/api_docs/kbn_esql_ast.mdx index 267849522ae82..d0396d3a27b20 100644 --- a/api_docs/kbn_esql_ast.mdx +++ b/api_docs/kbn_esql_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-ast title: "@kbn/esql-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-ast plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-ast'] --- import kbnEsqlAstObj from './kbn_esql_ast.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 269 | 1 | 211 | 34 | +| 270 | 1 | 211 | 35 | ## Common diff --git a/api_docs/kbn_esql_editor.mdx b/api_docs/kbn_esql_editor.mdx index c134c71bac836..faf4f4334fb3a 100644 --- a/api_docs/kbn_esql_editor.mdx +++ b/api_docs/kbn_esql_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-editor title: "@kbn/esql-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-editor plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-editor'] --- import kbnEsqlEditorObj from './kbn_esql_editor.devdocs.json'; diff --git a/api_docs/kbn_esql_utils.mdx b/api_docs/kbn_esql_utils.mdx index 10214e501594e..8b751f0cfe12f 100644 --- a/api_docs/kbn_esql_utils.mdx +++ b/api_docs/kbn_esql_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-utils title: "@kbn/esql-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-utils'] --- import kbnEsqlUtilsObj from './kbn_esql_utils.devdocs.json'; diff --git a/api_docs/kbn_esql_validation_autocomplete.devdocs.json b/api_docs/kbn_esql_validation_autocomplete.devdocs.json index ffc4814f5708c..601298c4f662e 100644 --- a/api_docs/kbn_esql_validation_autocomplete.devdocs.json +++ b/api_docs/kbn_esql_validation_autocomplete.devdocs.json @@ -400,7 +400,7 @@ "section": "def-common.CommandDefinition", "text": "CommandDefinition" }, - "[]" + "[]" ], "path": "packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts", "deprecated": false, @@ -598,7 +598,7 @@ "section": "def-common.ESQLCommandMode", "text": "ESQLCommandMode" }, - " | undefined; } | { type: \"option\"; command: ", + " | undefined; } | { type: \"setting\"; command: ", { "pluginId": "@kbn/esql-ast", "scope": "common", @@ -615,24 +615,6 @@ "text": "ESQLAstMetricsCommand" }, "; node: ", - { - "pluginId": "@kbn/esql-ast", - "scope": "common", - "docId": "kibKbnEsqlAstPluginApi", - "section": "def-common.ESQLFunction", - "text": "ESQLFunction" - }, - "<", - "FunctionSubtype", - ", string> | ", - { - "pluginId": "@kbn/esql-ast", - "scope": "common", - "docId": "kibKbnEsqlAstPluginApi", - "section": "def-common.ESQLCommandOption", - "text": "ESQLCommandOption" - }, - " | ", { "pluginId": "@kbn/esql-ast", "scope": "common", @@ -640,62 +622,6 @@ "section": "def-common.ESQLSource", "text": "ESQLSource" }, - " | ", - { - "pluginId": "@kbn/esql-ast", - "scope": "common", - "docId": "kibKbnEsqlAstPluginApi", - "section": "def-common.ESQLColumn", - "text": "ESQLColumn" - }, - " | ", - { - "pluginId": "@kbn/esql-ast", - "scope": "common", - "docId": "kibKbnEsqlAstPluginApi", - "section": "def-common.ESQLTimeInterval", - "text": "ESQLTimeInterval" - }, - " | ", - "ESQLList", - " | ", - "ESQLDecimalLiteral", - " | ", - "ESQLIntegerLiteral", - " | ", - "ESQLBooleanLiteral", - " | ", - "ESQLNullLiteral", - " | ", - { - "pluginId": "@kbn/esql-ast", - "scope": "common", - "docId": "kibKbnEsqlAstPluginApi", - "section": "def-common.ESQLParamLiteral", - "text": "ESQLParamLiteral" - }, - " | ", - { - "pluginId": "@kbn/esql-ast", - "scope": "common", - "docId": "kibKbnEsqlAstPluginApi", - "section": "def-common.ESQLCommandMode", - "text": "ESQLCommandMode" - }, - " | ", - "ESQLInlineCast", - "<", - { - "pluginId": "@kbn/esql-ast", - "scope": "common", - "docId": "kibKbnEsqlAstPluginApi", - "section": "def-common.ESQLAstItem", - "text": "ESQLAstItem" - }, - "> | ", - "ESQLOrderExpression", - " | ", - "ESQLUnknownItem", "; option: ", { "pluginId": "@kbn/esql-ast", @@ -712,38 +638,6 @@ "section": "def-common.ESQLCommandMode", "text": "ESQLCommandMode" }, - " | undefined; } | { type: \"setting\"; command: ", - { - "pluginId": "@kbn/esql-ast", - "scope": "common", - "docId": "kibKbnEsqlAstPluginApi", - "section": "def-common.ESQLCommand", - "text": "ESQLCommand" - }, - " | ", - { - "pluginId": "@kbn/esql-ast", - "scope": "common", - "docId": "kibKbnEsqlAstPluginApi", - "section": "def-common.ESQLAstMetricsCommand", - "text": "ESQLAstMetricsCommand" - }, - "; node: ", - { - "pluginId": "@kbn/esql-ast", - "scope": "common", - "docId": "kibKbnEsqlAstPluginApi", - "section": "def-common.ESQLSource", - "text": "ESQLSource" - }, - "; option: undefined; setting: ", - { - "pluginId": "@kbn/esql-ast", - "scope": "common", - "docId": "kibKbnEsqlAstPluginApi", - "section": "def-common.ESQLCommandMode", - "text": "ESQLCommandMode" - }, " | undefined; } | { type: \"newCommand\"; command: undefined; node: ", { "pluginId": "@kbn/esql-ast", @@ -755,6 +649,14 @@ "<", "FunctionSubtype", ", string> | ", + { + "pluginId": "@kbn/esql-ast", + "scope": "common", + "docId": "kibKbnEsqlAstPluginApi", + "section": "def-common.ESQLCommandOption", + "text": "ESQLCommandOption" + }, + " | ", { "pluginId": "@kbn/esql-ast", "scope": "common", @@ -797,6 +699,8 @@ "text": "ESQLParamLiteral" }, " | ", + "ESQLIdentifier", + " | ", { "pluginId": "@kbn/esql-ast", "scope": "common", @@ -861,6 +765,14 @@ "<", "FunctionSubtype", ", string> | ", + { + "pluginId": "@kbn/esql-ast", + "scope": "common", + "docId": "kibKbnEsqlAstPluginApi", + "section": "def-common.ESQLCommandOption", + "text": "ESQLCommandOption" + }, + " | ", { "pluginId": "@kbn/esql-ast", "scope": "common", @@ -903,6 +815,8 @@ "text": "ESQLParamLiteral" }, " | ", + "ESQLIdentifier", + " | ", { "pluginId": "@kbn/esql-ast", "scope": "common", @@ -975,6 +889,14 @@ "<", "FunctionSubtype", ", string> | ", + { + "pluginId": "@kbn/esql-ast", + "scope": "common", + "docId": "kibKbnEsqlAstPluginApi", + "section": "def-common.ESQLCommandOption", + "text": "ESQLCommandOption" + }, + " | ", { "pluginId": "@kbn/esql-ast", "scope": "common", @@ -1017,6 +939,8 @@ "text": "ESQLParamLiteral" }, " | ", + "ESQLIdentifier", + " | ", { "pluginId": "@kbn/esql-ast", "scope": "common", @@ -1206,7 +1130,8 @@ "docId": "kibKbnEsqlValidationAutocompletePluginApi", "section": "def-common.CommandDefinition", "text": "CommandDefinition" - } + }, + "" ], "path": "packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts", "deprecated": false, @@ -1247,7 +1172,7 @@ "section": "def-common.CommandOptionsDefinition", "text": "CommandOptionsDefinition" }, - " | undefined" + " | undefined" ], "path": "packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts", "deprecated": false, @@ -2705,34 +2630,14 @@ "section": "def-common.CommandDefinition", "text": "CommandDefinition" }, - " extends ", - "CommandBaseDefinition" + " extends ", + "CommandBaseDefinition", + "" ], "path": "packages/kbn-esql-validation-autocomplete/src/definitions/types.ts", "deprecated": false, "trackAdoption": false, "children": [ - { - "parentPluginId": "@kbn/esql-validation-autocomplete", - "id": "def-common.CommandDefinition.options", - "type": "Array", - "tags": [], - "label": "options", - "description": [], - "signature": [ - { - "pluginId": "@kbn/esql-validation-autocomplete", - "scope": "common", - "docId": "kibKbnEsqlValidationAutocompletePluginApi", - "section": "def-common.CommandOptionsDefinition", - "text": "CommandOptionsDefinition" - }, - "[]" - ], - "path": "packages/kbn-esql-validation-autocomplete/src/definitions/types.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "@kbn/esql-validation-autocomplete", "id": "def-common.CommandDefinition.examples", @@ -2802,11 +2707,27 @@ ], "returnComment": [] }, + { + "parentPluginId": "@kbn/esql-validation-autocomplete", + "id": "def-common.CommandDefinition.hasRecommendedQueries", + "type": "CompoundType", + "tags": [], + "label": "hasRecommendedQueries", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-esql-validation-autocomplete/src/definitions/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/esql-validation-autocomplete", "id": "def-common.CommandDefinition.modes", "type": "Array", - "tags": [], + "tags": [ + "deprecated" + ], "label": "modes", "description": [], "signature": [ @@ -2820,22 +2741,38 @@ "[]" ], "path": "packages/kbn-esql-validation-autocomplete/src/definitions/types.ts", - "deprecated": false, - "trackAdoption": false + "deprecated": true, + "trackAdoption": false, + "references": [ + { + "plugin": "@kbn/monaco", + "path": "packages/kbn-monaco/src/esql/lib/hover/hover.ts" + } + ] }, { "parentPluginId": "@kbn/esql-validation-autocomplete", - "id": "def-common.CommandDefinition.hasRecommendedQueries", - "type": "CompoundType", - "tags": [], - "label": "hasRecommendedQueries", + "id": "def-common.CommandDefinition.options", + "type": "Array", + "tags": [ + "deprecated" + ], + "label": "options", "description": [], "signature": [ - "boolean | undefined" + { + "pluginId": "@kbn/esql-validation-autocomplete", + "scope": "common", + "docId": "kibKbnEsqlValidationAutocompletePluginApi", + "section": "def-common.CommandOptionsDefinition", + "text": "CommandOptionsDefinition" + }, + "[]" ], "path": "packages/kbn-esql-validation-autocomplete/src/definitions/types.ts", - "deprecated": false, - "trackAdoption": false + "deprecated": true, + "trackAdoption": false, + "references": [] } ], "initialIsOpen": false @@ -2919,8 +2856,9 @@ "section": "def-common.CommandOptionsDefinition", "text": "CommandOptionsDefinition" }, - " extends ", - "CommandBaseDefinition" + " extends ", + "CommandBaseDefinition", + "" ], "path": "packages/kbn-esql-validation-autocomplete/src/definitions/types.ts", "deprecated": false, @@ -3096,10 +3034,10 @@ }, { "parentPluginId": "@kbn/esql-validation-autocomplete", - "id": "def-common.ESQLCallbacks.getFieldsFor", + "id": "def-common.ESQLCallbacks.getColumnsFor", "type": "Function", "tags": [], - "label": "getFieldsFor", + "label": "getColumnsFor", "description": [], "signature": [ "CallbackFn<{ query: string; }, ", diff --git a/api_docs/kbn_esql_validation_autocomplete.mdx b/api_docs/kbn_esql_validation_autocomplete.mdx index 36d6fef6be43a..f88883601f019 100644 --- a/api_docs/kbn_esql_validation_autocomplete.mdx +++ b/api_docs/kbn_esql_validation_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-validation-autocomplete title: "@kbn/esql-validation-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-validation-autocomplete plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-validation-autocomplete'] --- import kbnEsqlValidationAutocompleteObj from './kbn_esql_validation_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_common.mdx b/api_docs/kbn_event_annotation_common.mdx index 60da135a28c8d..a5b42c806e225 100644 --- a/api_docs/kbn_event_annotation_common.mdx +++ b/api_docs/kbn_event_annotation_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-common title: "@kbn/event-annotation-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-common plugin -date: 2024-10-29 +date: 2024-11-05 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 a6bf55226438b..9d3688691f294 100644 --- a/api_docs/kbn_event_annotation_components.mdx +++ b/api_docs/kbn_event_annotation_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-components title: "@kbn/event-annotation-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-components plugin -date: 2024-10-29 +date: 2024-11-05 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 f9a15dcf85cd4..e3c5ce3e147d7 100644 --- a/api_docs/kbn_expandable_flyout.mdx +++ b/api_docs/kbn_expandable_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-expandable-flyout title: "@kbn/expandable-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/expandable-flyout plugin -date: 2024-10-29 +date: 2024-11-05 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 78eddd960ee16..0855785dc7caf 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2024-10-29 +date: 2024-11-05 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 8e19898fe0ae4..16363a179caf8 100644 --- a/api_docs/kbn_field_utils.mdx +++ b/api_docs/kbn_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-utils title: "@kbn/field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-utils plugin -date: 2024-10-29 +date: 2024-11-05 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 39ef3da6761a3..3ad5ed6198db5 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_formatters.mdx b/api_docs/kbn_formatters.mdx index f28f011ca7a43..9729d5c0a45e3 100644 --- a/api_docs/kbn_formatters.mdx +++ b/api_docs/kbn_formatters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-formatters title: "@kbn/formatters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/formatters plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/formatters'] --- import kbnFormattersObj from './kbn_formatters.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index 553eb3f9c1afa..269f132d8adf5 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_ui_services.mdx b/api_docs/kbn_ftr_common_functional_ui_services.mdx index a09b0d8459328..8b3bd0de28eb2 100644 --- a/api_docs/kbn_ftr_common_functional_ui_services.mdx +++ b/api_docs/kbn_ftr_common_functional_ui_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-ui-services title: "@kbn/ftr-common-functional-ui-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-ui-services plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-ui-services'] --- import kbnFtrCommonFunctionalUiServicesObj from './kbn_ftr_common_functional_ui_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index dd58296bf4e8d..be114f8da4d22 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2024-10-29 +date: 2024-11-05 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 a56c3bf13069c..917e28a54505f 100644 --- a/api_docs/kbn_generate_console_definitions.mdx +++ b/api_docs/kbn_generate_console_definitions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-console-definitions title: "@kbn/generate-console-definitions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-console-definitions plugin -date: 2024-10-29 +date: 2024-11-05 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 27b20ca3fb6f4..670b0500e0599 100644 --- a/api_docs/kbn_generate_csv.mdx +++ b/api_docs/kbn_generate_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv title: "@kbn/generate-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_grid_layout.mdx b/api_docs/kbn_grid_layout.mdx index 5b6fcfb09eb9d..0b74d30ac6bc5 100644 --- a/api_docs/kbn_grid_layout.mdx +++ b/api_docs/kbn_grid_layout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-grid-layout title: "@kbn/grid-layout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/grid-layout plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/grid-layout'] --- import kbnGridLayoutObj from './kbn_grid_layout.devdocs.json'; diff --git a/api_docs/kbn_grouping.mdx b/api_docs/kbn_grouping.mdx index 43e9d9483c53f..f8ae6f8a12498 100644 --- a/api_docs/kbn_grouping.mdx +++ b/api_docs/kbn_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-grouping title: "@kbn/grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/grouping plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/grouping'] --- import kbnGroupingObj from './kbn_grouping.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index ab24bb6657d03..52c84e611789f 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2024-10-29 +date: 2024-11-05 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 2d67ce9788e73..b18e62fbc5a70 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2024-10-29 +date: 2024-11-05 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 36454569a138a..834f02dfae821 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 8468002692341..c413a143b7158 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2024-10-29 +date: 2024-11-05 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 0091c62287e6b..e7d02c9808f6a 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2024-10-29 +date: 2024-11-05 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 37a43ae2ea00b..14d3c9808799f 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2024-10-29 +date: 2024-11-05 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 a9a892a8b09b2..2bbd69c69e716 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2024-10-29 +date: 2024-11-05 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 78731f24e44b7..19852b063e8f9 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2024-10-29 +date: 2024-11-05 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 0355aeb742946..ea1aed87b262a 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_index_management_shared_types.mdx b/api_docs/kbn_index_management_shared_types.mdx index ce8311f0a9759..37b896018af11 100644 --- a/api_docs/kbn_index_management_shared_types.mdx +++ b/api_docs/kbn_index_management_shared_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-index-management-shared-types title: "@kbn/index-management-shared-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/index-management-shared-types plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/index-management-shared-types'] --- import kbnIndexManagementSharedTypesObj from './kbn_index_management_shared_types.devdocs.json'; diff --git a/api_docs/kbn_inference_common.devdocs.json b/api_docs/kbn_inference_common.devdocs.json new file mode 100644 index 0000000000000..4deeb465f77fb --- /dev/null +++ b/api_docs/kbn_inference_common.devdocs.json @@ -0,0 +1,2307 @@ +{ + "id": "@kbn/inference-common", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.InferenceTaskError", + "type": "Class", + "tags": [], + "label": "InferenceTaskError", + "description": [ + "\nBase class for all inference API errors." + ], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskError", + "text": "InferenceTaskError" + }, + " extends Error" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.InferenceTaskError.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.InferenceTaskError.Unnamed.$1", + "type": "Uncategorized", + "tags": [], + "label": "code", + "description": [], + "signature": [ + "TCode" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.InferenceTaskError.Unnamed.$2", + "type": "string", + "tags": [], + "label": "message", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.InferenceTaskError.Unnamed.$3", + "type": "Uncategorized", + "tags": [], + "label": "meta", + "description": [], + "signature": [ + "TMeta" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.InferenceTaskError.toJSON", + "type": "Function", + "tags": [], + "label": "toJSON", + "description": [], + "signature": [ + "() => ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskErrorEvent", + "text": "InferenceTaskErrorEvent" + } + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.createInferenceInternalError", + "type": "Function", + "tags": [], + "label": "createInferenceInternalError", + "description": [], + "signature": [ + "(message: string, meta: Record | undefined) => ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskInternalError", + "text": "InferenceTaskInternalError" + } + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.createInferenceInternalError.$1", + "type": "string", + "tags": [], + "label": "message", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.createInferenceInternalError.$2", + "type": "Object", + "tags": [], + "label": "meta", + "description": [], + "signature": [ + "Record | undefined" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.createInferenceRequestError", + "type": "Function", + "tags": [], + "label": "createInferenceRequestError", + "description": [], + "signature": [ + "(message: string, status: number) => ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskRequestError", + "text": "InferenceTaskRequestError" + } + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.createInferenceRequestError.$1", + "type": "string", + "tags": [], + "label": "message", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.createInferenceRequestError.$2", + "type": "number", + "tags": [], + "label": "status", + "description": [], + "signature": [ + "number" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isChatCompletionChunkEvent", + "type": "Function", + "tags": [], + "label": "isChatCompletionChunkEvent", + "description": [ + "\nCheck if the provided {@link ChatCompletionEvent} is a {@link ChatCompletionChunkEvent}" + ], + "signature": [ + "(event: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionEvent", + "text": "ChatCompletionEvent" + }, + "<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ToolOptions", + "text": "ToolOptions" + }, + ">) => boolean" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/event_utils.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isChatCompletionChunkEvent.$1", + "type": "CompoundType", + "tags": [], + "label": "event", + "description": [], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionEvent", + "text": "ChatCompletionEvent" + }, + "<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ToolOptions", + "text": "ToolOptions" + }, + ">" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/event_utils.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isChatCompletionEvent", + "type": "Function", + "tags": [], + "label": "isChatCompletionEvent", + "description": [ + "\nCheck if the provided {@link InferenceTaskEvent} is a {@link ChatCompletionEvent}" + ], + "signature": [ + "(event: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskEvent", + "text": "InferenceTaskEvent" + }, + ") => boolean" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/event_utils.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isChatCompletionEvent.$1", + "type": "Object", + "tags": [], + "label": "event", + "description": [], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskEvent", + "text": "InferenceTaskEvent" + } + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/event_utils.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isChatCompletionMessageEvent", + "type": "Function", + "tags": [], + "label": "isChatCompletionMessageEvent", + "description": [ + "\nCheck if the provided {@link ChatCompletionEvent} is a {@link ChatCompletionMessageEvent}" + ], + "signature": [ + "(event: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionEvent", + "text": "ChatCompletionEvent" + }, + ") => boolean" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/event_utils.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isChatCompletionMessageEvent.$1", + "type": "CompoundType", + "tags": [], + "label": "event", + "description": [], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionEvent", + "text": "ChatCompletionEvent" + }, + "" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/event_utils.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isChatCompletionTokenCountEvent", + "type": "Function", + "tags": [], + "label": "isChatCompletionTokenCountEvent", + "description": [ + "\nCheck if the provided {@link ChatCompletionEvent} is a {@link ChatCompletionMessageEvent}" + ], + "signature": [ + "(event: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionEvent", + "text": "ChatCompletionEvent" + }, + "<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ToolOptions", + "text": "ToolOptions" + }, + ">) => boolean" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/event_utils.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isChatCompletionTokenCountEvent.$1", + "type": "CompoundType", + "tags": [], + "label": "event", + "description": [], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionEvent", + "text": "ChatCompletionEvent" + }, + "<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ToolOptions", + "text": "ToolOptions" + }, + ">" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/event_utils.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isInferenceError", + "type": "Function", + "tags": [], + "label": "isInferenceError", + "description": [], + "signature": [ + "(error: unknown) => boolean" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isInferenceError.$1", + "type": "Unknown", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "unknown" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isInferenceInternalError", + "type": "Function", + "tags": [], + "label": "isInferenceInternalError", + "description": [], + "signature": [ + "(error: unknown) => boolean" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isInferenceInternalError.$1", + "type": "Unknown", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "unknown" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isInferenceRequestError", + "type": "Function", + "tags": [], + "label": "isInferenceRequestError", + "description": [], + "signature": [ + "(error: unknown) => boolean" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isInferenceRequestError.$1", + "type": "Unknown", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "unknown" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isOutputCompleteEvent", + "type": "Function", + "tags": [], + "label": "isOutputCompleteEvent", + "description": [ + "\nCheck if the provided {@link ChatCompletionEvent} is a {@link ChatCompletionChunkEvent}" + ], + "signature": [ + "(event: TOutputEvent) => boolean" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/output/event_utils.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isOutputCompleteEvent.$1", + "type": "Uncategorized", + "tags": [], + "label": "event", + "description": [], + "signature": [ + "TOutputEvent" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/output/event_utils.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isOutputEvent", + "type": "Function", + "tags": [], + "label": "isOutputEvent", + "description": [ + "\nCheck if the provided {@link InferenceTaskEvent} is a {@link OutputEvent}" + ], + "signature": [ + "(event: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskEvent", + "text": "InferenceTaskEvent" + }, + ") => boolean" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/output/event_utils.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isOutputEvent.$1", + "type": "Object", + "tags": [], + "label": "event", + "description": [], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskEvent", + "text": "InferenceTaskEvent" + } + ], + "path": "x-pack/packages/ai-infra/inference-common/src/output/event_utils.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isOutputUpdateEvent", + "type": "Function", + "tags": [], + "label": "isOutputUpdateEvent", + "description": [ + "\nCheck if the provided {@link OutputEvent} is a {@link OutputUpdateEvent}" + ], + "signature": [ + "(event: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.OutputEvent", + "text": "OutputEvent" + }, + ") => boolean" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/output/event_utils.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isOutputUpdateEvent.$1", + "type": "CompoundType", + "tags": [], + "label": "event", + "description": [], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.OutputEvent", + "text": "OutputEvent" + }, + "" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/output/event_utils.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isTokenLimitReachedError", + "type": "Function", + "tags": [], + "label": "isTokenLimitReachedError", + "description": [ + "\nCheck if an error is a {@link ChatCompletionTokenLimitReachedError}" + ], + "signature": [ + "(error: Error) => boolean" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isTokenLimitReachedError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/errors.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isToolNotFoundError", + "type": "Function", + "tags": [], + "label": "isToolNotFoundError", + "description": [ + "\nCheck if an error is a {@link ChatCompletionToolNotFoundError}" + ], + "signature": [ + "(error: Error) => boolean" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isToolNotFoundError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/errors.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isToolValidationError", + "type": "Function", + "tags": [], + "label": "isToolValidationError", + "description": [ + "\nCheck if an error is a {@link ChatCompletionToolValidationError}" + ], + "signature": [ + "(error: Error | undefined) => boolean" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.isToolValidationError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | undefined" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/errors.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.withoutChunkEvents", + "type": "Function", + "tags": [], + "label": "withoutChunkEvents", + "description": [ + "\nOperator filtering out the chunk events from the provided observable." + ], + "signature": [ + "() => ", + "OperatorFunction", + ">" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/event_utils.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.withoutOutputUpdateEvents", + "type": "Function", + "tags": [], + "label": "withoutOutputUpdateEvents", + "description": [ + "\nOperator filtering out the update events from the provided observable." + ], + "signature": [ + "() => ", + "OperatorFunction", + ">>" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/output/event_utils.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.withoutTokenCountEvents", + "type": "Function", + "tags": [], + "label": "withoutTokenCountEvents", + "description": [ + "\nOperator filtering out the token count events from the provided observable." + ], + "signature": [ + "() => ", + "OperatorFunction", + ">" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/event_utils.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ChatCompletionChunkToolCall", + "type": "Interface", + "tags": [], + "label": "ChatCompletionChunkToolCall", + "description": [ + "\nRepresent a partial tool call present in a chunk event.\n\nNote that all properties of the structure, except from the index,\nare partial and must be aggregated." + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/events.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ChatCompletionChunkToolCall.index", + "type": "number", + "tags": [], + "label": "index", + "description": [ + "\nThe tool call index (position in the tool call array)." + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ChatCompletionChunkToolCall.toolCallId", + "type": "string", + "tags": [], + "label": "toolCallId", + "description": [ + "\nchunk of tool call id." + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ChatCompletionChunkToolCall.function", + "type": "Object", + "tags": [], + "label": "function", + "description": [], + "signature": [ + "{ name: string; arguments: string; }" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/events.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.InferenceTaskEventBase", + "type": "Interface", + "tags": [], + "label": "InferenceTaskEventBase", + "description": [ + "\nBase interface for all inference events." + ], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskEventBase", + "text": "InferenceTaskEventBase" + }, + "" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/inference_task.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.InferenceTaskEventBase.type", + "type": "Uncategorized", + "tags": [], + "label": "type", + "description": [ + "\nUnique identifier of the event type." + ], + "signature": [ + "TEventType" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/inference_task.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ToolCall", + "type": "Interface", + "tags": [], + "label": "ToolCall", + "description": [ + "\nRepresents a tool call performed by the LLM." + ], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ToolCall", + "text": "ToolCall" + }, + "" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/tools.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ToolCall.toolCallId", + "type": "string", + "tags": [], + "label": "toolCallId", + "description": [ + "\nThe id of the tool call, that must be re-used when providing the tool call response" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/tools.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ToolCall.function", + "type": "CompoundType", + "tags": [], + "label": "function", + "description": [], + "signature": [ + "{ name: TName; } & (TArguments extends Record ? { arguments: TArguments; } : {})" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/tools.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ToolDefinition", + "type": "Interface", + "tags": [], + "label": "ToolDefinition", + "description": [ + "\nThe definition of a tool that will be provided to the LLM for it to eventually call." + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/tools.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ToolDefinition.description", + "type": "string", + "tags": [], + "label": "description", + "description": [ + "\nA description of what the tool does. Note that this will be exposed to the LLM,\nso the description should be explicit about what the tool does and when to call it." + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/tools.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ToolDefinition.schema", + "type": "Object", + "tags": [], + "label": "schema", + "description": [ + "\nThe input schema for the tool, representing the shape of the tool's parameters\n\nEven if optional, it is highly recommended to define a schema for all tool definitions, unless\nthe tool is supposed to be called without parameters." + ], + "signature": [ + "ToolSchemaTypeObject | undefined" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/tools.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ToolOptions", + "type": "Interface", + "tags": [], + "label": "ToolOptions", + "description": [ + "\nTool-related parameters of {@link ChatCompleteAPI}" + ], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ToolOptions", + "text": "ToolOptions" + }, + "" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/tools.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ToolOptions.toolChoice", + "type": "CompoundType", + "tags": [], + "label": "toolChoice", + "description": [ + "\nThe choice of tool execution.\n\nRefer to {@link ToolChoice}" + ], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ToolChoice", + "text": "ToolChoice" + }, + " | undefined" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/tools.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ToolOptions.tools", + "type": "Object", + "tags": [], + "label": "tools", + "description": [ + "\nThe list of tool definitions that will be exposed to the LLM.\n\nRefer to {@link ToolDefinition}." + ], + "signature": [ + "Record | undefined" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/tools.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.UnvalidatedToolCall", + "type": "Interface", + "tags": [], + "label": "UnvalidatedToolCall", + "description": [ + "\nRepresents a tool call from the LLM before correctly converted to the schema type.\n\nOnly publicly exposed because referenced by {@link ChatCompletionToolValidationError}" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/tools.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.UnvalidatedToolCall.toolCallId", + "type": "string", + "tags": [], + "label": "toolCallId", + "description": [], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/tools.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.UnvalidatedToolCall.function", + "type": "Object", + "tags": [], + "label": "function", + "description": [], + "signature": [ + "{ name: string; arguments: string; }" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/tools.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ChatCompletionErrorCode", + "type": "Enum", + "tags": [], + "label": "ChatCompletionErrorCode", + "description": [ + "\nList of code of error that are specific to the {@link ChatCompleteAPI}" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/errors.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ChatCompletionEventType", + "type": "Enum", + "tags": [], + "label": "ChatCompletionEventType", + "description": [ + "\nList possible values of {@link ChatCompletionEvent} types." + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/events.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.InferenceTaskErrorCode", + "type": "Enum", + "tags": [], + "label": "InferenceTaskErrorCode", + "description": [ + "\nEnum for generic inference error codes." + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.MessageRole", + "type": "Enum", + "tags": [], + "label": "MessageRole", + "description": [ + "\nEnum for all possible {@link Message} roles." + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/messages.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.OutputEventType", + "type": "Enum", + "tags": [], + "label": "OutputEventType", + "description": [ + "\nList possible values of {@link OutputEvent} types." + ], + "path": "x-pack/packages/ai-infra/inference-common/src/output/events.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ToolChoiceType", + "type": "Enum", + "tags": [], + "label": "ToolChoiceType", + "description": [ + "\nTool invocation choice type.\n\nRefer to {@link ToolChoice} for more details." + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/tools.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "misc": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.AssistantMessage", + "type": "Type", + "tags": [], + "label": "AssistantMessage", + "description": [ + "\nRepresents a message from the LLM." + ], + "signature": [ + "MessageBase<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.MessageRole", + "text": "MessageRole" + }, + ".Assistant> & { content: string | null; toolCalls?: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ToolCall", + "text": "ToolCall" + }, + " | undefined>[] | undefined; }" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/messages.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ChatCompleteAPI", + "type": "Type", + "tags": [], + "label": "ChatCompleteAPI", + "description": [ + "\nRequest a completion from the LLM based on a prompt or conversation.\n" + ], + "signature": [ + " = ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ToolOptions", + "text": "ToolOptions" + }, + ">(options: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompleteOptions", + "text": "ChatCompleteOptions" + }, + ") => ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionResponse", + "text": "ChatCompletionResponse" + }, + "" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/api.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ChatCompleteAPI.$1", + "type": "CompoundType", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "{ connectorId: string; system?: string | undefined; messages: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + "[]; functionCalling?: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.FunctionCallingMode", + "text": "FunctionCallingMode" + }, + " | undefined; } & TToolOptions" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/api.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ChatCompleteOptions", + "type": "Type", + "tags": [], + "label": "ChatCompleteOptions", + "description": [ + "\nOptions used to call the {@link ChatCompleteAPI}" + ], + "signature": [ + "{ connectorId: string; system?: string | undefined; messages: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + "[]; functionCalling?: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.FunctionCallingMode", + "text": "FunctionCallingMode" + }, + " | undefined; } & TToolOptions" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/api.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ChatCompletionChunkEvent", + "type": "Type", + "tags": [], + "label": "ChatCompletionChunkEvent", + "description": [ + "\nChunk event, containing a fragment of the total content,\nand potentially chunks of tool calls." + ], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskEventBase", + "text": "InferenceTaskEventBase" + }, + "<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionEventType", + "text": "ChatCompletionEventType" + }, + ".ChatCompletionChunk> & { content: string; tool_calls: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionChunkToolCall", + "text": "ChatCompletionChunkToolCall" + }, + "[]; }" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/events.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ChatCompletionEvent", + "type": "Type", + "tags": [], + "label": "ChatCompletionEvent", + "description": [ + "\nEvents emitted from the {@link ChatCompletionResponse} observable\nreturned from the {@link ChatCompleteAPI}.\n\nThe chatComplete API returns 3 type of events:\n- {@link ChatCompletionChunkEvent}: message chunk events\n- {@link ChatCompletionTokenCountEvent}: token count event\n- {@link ChatCompletionMessageEvent}: message event\n\nNote that chunk events can be emitted any amount of times, but token count will be emitted\nat most once (could not be emitted depending on the underlying connector), and message\nevent will be emitted ex\n" + ], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionChunkEvent", + "text": "ChatCompletionChunkEvent" + }, + " | ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionTokenCountEvent", + "text": "ChatCompletionTokenCountEvent" + }, + " | ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionMessageEvent", + "text": "ChatCompletionMessageEvent" + }, + "" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/events.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ChatCompletionMessageEvent", + "type": "Type", + "tags": [], + "label": "ChatCompletionMessageEvent", + "description": [ + "\nMessage event, sent only once, after all the chunks were emitted, and containing\nthe whole text content and potential tool calls of the response." + ], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskEventBase", + "text": "InferenceTaskEventBase" + }, + "<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionEventType", + "text": "ChatCompletionEventType" + }, + ".ChatCompletionMessage> & { content: string; toolCalls: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ToolCallsOf", + "text": "ToolCallsOf" + }, + "[\"toolCalls\"]; }" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/events.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ChatCompletionResponse", + "type": "Type", + "tags": [], + "label": "ChatCompletionResponse", + "description": [ + "\nResponse from the {@link ChatCompleteAPI}.\n\nObservable of {@link ChatCompletionEvent}" + ], + "signature": [ + "Observable", + "<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionEvent", + "text": "ChatCompletionEvent" + }, + ">" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/api.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ChatCompletionTokenCountEvent", + "type": "Type", + "tags": [], + "label": "ChatCompletionTokenCountEvent", + "description": [ + "\nToken count event, send only once, usually (but not necessarily)\nbefore the message event" + ], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskEventBase", + "text": "InferenceTaskEventBase" + }, + "<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionEventType", + "text": "ChatCompletionEventType" + }, + ".ChatCompletionTokenCount> & { tokens: { prompt: number; completion: number; total: number; }; }" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/events.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ChatCompletionTokenLimitReachedError", + "type": "Type", + "tags": [], + "label": "ChatCompletionTokenLimitReachedError", + "description": [ + "\nError thrown if the completion call fails because of a token limit\nerror, e.g. when the context window is higher than the limit" + ], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskError", + "text": "InferenceTaskError" + }, + "<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionErrorCode", + "text": "ChatCompletionErrorCode" + }, + ".TokenLimitReachedError, { tokenLimit?: number | undefined; tokenCount?: number | undefined; }>" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/errors.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ChatCompletionToolNotFoundError", + "type": "Type", + "tags": [], + "label": "ChatCompletionToolNotFoundError", + "description": [ + "\nError thrown if the LLM called a tool that was not provided\nin the list of available tools." + ], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskError", + "text": "InferenceTaskError" + }, + "<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionErrorCode", + "text": "ChatCompletionErrorCode" + }, + ".ToolNotFoundError, { name: string; }>" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/errors.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ChatCompletionToolValidationError", + "type": "Type", + "tags": [], + "label": "ChatCompletionToolValidationError", + "description": [ + "\nError thrown when the LLM called a tool with parameters that\ndon't match the tool's schema.\n\nThe level of details on the error vary depending on the underlying LLM." + ], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskError", + "text": "InferenceTaskError" + }, + "<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ChatCompletionErrorCode", + "text": "ChatCompletionErrorCode" + }, + ".ToolValidationError, { name?: string | undefined; arguments?: string | undefined; errorsText?: string | undefined; toolCalls?: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.UnvalidatedToolCall", + "text": "UnvalidatedToolCall" + }, + "[] | undefined; }>" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/errors.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.FromToolSchema", + "type": "Type", + "tags": [], + "label": "FromToolSchema", + "description": [ + "\nUtility type to infer the shape of a tool call from its schema." + ], + "signature": [ + "TToolSchema extends ToolSchemaTypeObject ? FromToolSchemaObject : TToolSchema extends ToolSchemaTypeArray ? FromToolSchemaArray : TToolSchema extends ToolSchemaTypeBoolean ? boolean : TToolSchema extends ToolSchemaTypeNumber ? number : TToolSchema extends ToolSchemaTypeString ? FromToolSchemaString : never" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/tool_schema.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.FunctionCallingMode", + "type": "Type", + "tags": [], + "label": "FunctionCallingMode", + "description": [ + "\nDefine the function calling mode when using inference APIs.\n- native will use the LLM's native function calling (requires the LLM to have native support)\n- simulated: will emulate function calling with function calling instructions" + ], + "signature": [ + "\"native\" | \"simulated\"" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/api.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.InferenceTaskErrorEvent", + "type": "Type", + "tags": [], + "label": "InferenceTaskErrorEvent", + "description": [], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskEventBase", + "text": "InferenceTaskEventBase" + }, + "<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskEventType", + "text": "InferenceTaskEventType" + }, + "> & { error: { code: string; message: string; meta?: Record | undefined; }; }" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.InferenceTaskEvent", + "type": "Type", + "tags": [], + "label": "InferenceTaskEvent", + "description": [], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskEventBase", + "text": "InferenceTaskEventBase" + }, + "" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/inference_task.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.InferenceTaskEventType", + "type": "string", + "tags": [], + "label": "InferenceTaskEventType", + "description": [], + "path": "x-pack/packages/ai-infra/inference-common/src/inference_task.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.InferenceTaskInternalError", + "type": "Type", + "tags": [], + "label": "InferenceTaskInternalError", + "description": [], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskError", + "text": "InferenceTaskError" + }, + "<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskErrorCode", + "text": "InferenceTaskErrorCode" + }, + ".internalError, Record>" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.InferenceTaskRequestError", + "type": "Type", + "tags": [], + "label": "InferenceTaskRequestError", + "description": [], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskError", + "text": "InferenceTaskError" + }, + "<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskErrorCode", + "text": "InferenceTaskErrorCode" + }, + ".requestError, { status: number; }>" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/errors.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.Message", + "type": "Type", + "tags": [], + "label": "Message", + "description": [ + "\nMixin composed of all the possible types of messages in a chatComplete discussion.\n\nMessage can be of three types:\n- {@link UserMessage}\n- {@link AssistantMessage}\n- {@link ToolMessage}" + ], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.UserMessage", + "text": "UserMessage" + }, + " | ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.AssistantMessage", + "text": "AssistantMessage" + }, + " | ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ToolMessage", + "text": "ToolMessage" + }, + "" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/messages.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.Output", + "type": "Type", + "tags": [], + "label": "Output", + "description": [ + "\nTask output of a {@link OutputCompleteEvent}" + ], + "signature": [ + "unknown" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/output/events.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.OutputAPI", + "type": "Type", + "tags": [], + "label": "OutputAPI", + "description": [ + "\nGenerate a response with the LLM for a prompt, optionally based on a schema.\n" + ], + "signature": [ + "(id: TId, options: { connectorId: string; system?: string | undefined; input: string; schema?: TOutputSchema | undefined; previousMessages?: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + "[] | undefined; functionCalling?: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.FunctionCallingMode", + "text": "FunctionCallingMode" + }, + " | undefined; }) => ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.OutputResponse", + "text": "OutputResponse" + }, + "" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/output/api.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.OutputAPI.$1", + "type": "Uncategorized", + "tags": [], + "label": "id", + "description": [ + "The id of the operation" + ], + "signature": [ + "TId" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/output/api.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.OutputAPI.$2", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "{ connectorId: string; system?: string | undefined; input: string; schema?: TOutputSchema | undefined; previousMessages?: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + "[] | undefined; functionCalling?: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.FunctionCallingMode", + "text": "FunctionCallingMode" + }, + " | undefined; }" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/output/api.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.OutputCompleteEvent", + "type": "Type", + "tags": [], + "label": "OutputCompleteEvent", + "description": [ + "\nCompletion (complete message) event for the {@link OutputAPI}" + ], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskEventBase", + "text": "InferenceTaskEventBase" + }, + "<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.OutputEventType", + "text": "OutputEventType" + }, + ".OutputComplete> & { id: TId; output: TOutput; content: string; }" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/output/events.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.OutputEvent", + "type": "Type", + "tags": [], + "label": "OutputEvent", + "description": [ + "\nEvents emitted from the {@link OutputEvent}." + ], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.OutputUpdateEvent", + "text": "OutputUpdateEvent" + }, + " | ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.OutputCompleteEvent", + "text": "OutputCompleteEvent" + }, + "" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/output/events.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.OutputResponse", + "type": "Type", + "tags": [], + "label": "OutputResponse", + "description": [ + "\nResponse from the {@link OutputAPI}.\n\nObservable of {@link OutputEvent}" + ], + "signature": [ + "Observable", + "<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.OutputEvent", + "text": "OutputEvent" + }, + " : undefined>>" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/output/api.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.OutputUpdateEvent", + "type": "Type", + "tags": [], + "label": "OutputUpdateEvent", + "description": [ + "\nUpdate (chunk) event for the {@link OutputAPI}" + ], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.InferenceTaskEventBase", + "text": "InferenceTaskEventBase" + }, + "<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.OutputEventType", + "text": "OutputEventType" + }, + ".OutputUpdate> & { id: TId; content: string; }" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/output/events.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ToolCallsOf", + "type": "Type", + "tags": [], + "label": "ToolCallsOf", + "description": [ + "\nUtility type to infer the toolCall type of {@link ChatCompletionMessageEvent}." + ], + "signature": [ + "TToolOptions extends { tools?: Record | undefined; } ? TToolOptions extends { toolChoice: ", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ToolChoiceType", + "text": "ToolChoiceType" + }, + ".none; } ? { toolCalls: []; } : { toolCalls: ToolResponsesOf>; } : { toolCalls: never; }" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/tools.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ToolChoice", + "type": "Type", + "tags": [], + "label": "ToolChoice", + "description": [ + "\nDefines the tool invocation for {@link ToolOptions}, either a {@link ToolChoiceType} or {@link CustomToolChoice}.\n- {@link ToolChoiceType.none}: the LLM will never call a tool\n- {@link ToolChoiceType.auto}: the LLM will decide if it should call a tool or provide a text response\n- {@link ToolChoiceType.required}: the LLM will always call a tool, but will decide with one to call\n- {@link CustomToolChoice}: the LLM will always call the specified tool" + ], + "signature": [ + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.ToolChoiceType", + "text": "ToolChoiceType" + }, + " | CustomToolChoice" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/tools.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ToolMessage", + "type": "Type", + "tags": [], + "label": "ToolMessage", + "description": [ + "\nRepresents a tool invocation result, following a request from the LLM to execute a tool." + ], + "signature": [ + "MessageBase<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.MessageRole", + "text": "MessageRole" + }, + ".Tool> & { toolCallId: string; response: TToolResponse; }" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/messages.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ToolSchema", + "type": "Type", + "tags": [], + "label": "ToolSchema", + "description": [ + "\nDefines the schema for a {@link ToolDefinition}" + ], + "signature": [ + "ToolSchemaTypeObject" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/tool_schema.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.ToolSchemaType", + "type": "Type", + "tags": [], + "label": "ToolSchemaType", + "description": [], + "signature": [ + "ToolSchemaTypeObject | ToolSchemaTypeString | ToolSchemaTypeBoolean | ToolSchemaTypeNumber | ToolSchemaTypeArray" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/tool_schema.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/inference-common", + "id": "def-common.UserMessage", + "type": "Type", + "tags": [], + "label": "UserMessage", + "description": [ + "\nRepresents a message from the user." + ], + "signature": [ + "MessageBase<", + { + "pluginId": "@kbn/inference-common", + "scope": "common", + "docId": "kibKbnInferenceCommonPluginApi", + "section": "def-common.MessageRole", + "text": "MessageRole" + }, + ".User> & { content: string; }" + ], + "path": "x-pack/packages/ai-infra/inference-common/src/chat_complete/messages.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_inference_common.mdx b/api_docs/kbn_inference_common.mdx new file mode 100644 index 0000000000000..1169da2685c52 --- /dev/null +++ b/api_docs/kbn_inference_common.mdx @@ -0,0 +1,42 @@ +--- +#### +#### 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: kibKbnInferenceCommonPluginApi +slug: /kibana-dev-docs/api/kbn-inference-common +title: "@kbn/inference-common" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/inference-common plugin +date: 2024-11-05 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/inference-common'] +--- +import kbnInferenceCommonObj from './kbn_inference_common.devdocs.json'; + + + +Contact [@elastic/appex-ai-infra](https://github.com/orgs/elastic/teams/appex-ai-infra) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 99 | 0 | 39 | 0 | + +## Common + +### Functions + + +### Classes + + +### Interfaces + + +### Enums + + +### Consts, variables and types + + diff --git a/api_docs/kbn_inference_integration_flyout.mdx b/api_docs/kbn_inference_integration_flyout.mdx index 4b358e7bea02e..1832b87f74402 100644 --- a/api_docs/kbn_inference_integration_flyout.mdx +++ b/api_docs/kbn_inference_integration_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-inference_integration_flyout title: "@kbn/inference_integration_flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/inference_integration_flyout plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/inference_integration_flyout'] --- import kbnInferenceIntegrationFlyoutObj from './kbn_inference_integration_flyout.devdocs.json'; diff --git a/api_docs/kbn_infra_forge.mdx b/api_docs/kbn_infra_forge.mdx index 8e9e63ef82c29..8f8a8073d7291 100644 --- a/api_docs/kbn_infra_forge.mdx +++ b/api_docs/kbn_infra_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-infra-forge title: "@kbn/infra-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/infra-forge plugin -date: 2024-10-29 +date: 2024-11-05 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 5623d6eb4cf25..10febd823c2f5 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_investigation_shared.mdx b/api_docs/kbn_investigation_shared.mdx index 90f07292320d9..651519d2db9c0 100644 --- a/api_docs/kbn_investigation_shared.mdx +++ b/api_docs/kbn_investigation_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-investigation-shared title: "@kbn/investigation-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/investigation-shared plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/investigation-shared'] --- import kbnInvestigationSharedObj from './kbn_investigation_shared.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 7a5d0822252a7..4c503ccbeb3a7 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_ipynb.mdx b/api_docs/kbn_ipynb.mdx index 272509c5ae8e2..5556907f40036 100644 --- a/api_docs/kbn_ipynb.mdx +++ b/api_docs/kbn_ipynb.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ipynb title: "@kbn/ipynb" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ipynb plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ipynb'] --- import kbnIpynbObj from './kbn_ipynb.devdocs.json'; diff --git a/api_docs/kbn_item_buffer.mdx b/api_docs/kbn_item_buffer.mdx index 844e0fa6f41e6..5e22882c74490 100644 --- a/api_docs/kbn_item_buffer.mdx +++ b/api_docs/kbn_item_buffer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-item-buffer title: "@kbn/item-buffer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/item-buffer plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/item-buffer'] --- import kbnItemBufferObj from './kbn_item_buffer.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index e97688ee43d86..a3a71c439bee0 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2024-10-29 +date: 2024-11-05 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 6d1dad23e051b..afedbb6c915a5 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2024-10-29 +date: 2024-11-05 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 7718b89474716..32f7b331e2449 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-ast'] --- import kbnJsonAstObj from './kbn_json_ast.devdocs.json'; diff --git a/api_docs/kbn_json_schemas.mdx b/api_docs/kbn_json_schemas.mdx index 9e6f4c5f450b6..aaa22be8f62a2 100644 --- a/api_docs/kbn_json_schemas.mdx +++ b/api_docs/kbn_json_schemas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-schemas title: "@kbn/json-schemas" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-schemas plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-schemas'] --- import kbnJsonSchemasObj from './kbn_json_schemas.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index 5ff460560264d..b23761eb34109 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2024-10-29 +date: 2024-11-05 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.mdx b/api_docs/kbn_language_documentation.mdx index 4bb0a3d649cc8..57a0baf2102e1 100644 --- a/api_docs/kbn_language_documentation.mdx +++ b/api_docs/kbn_language_documentation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation title: "@kbn/language-documentation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation'] --- import kbnLanguageDocumentationObj from './kbn_language_documentation.devdocs.json'; diff --git a/api_docs/kbn_lens_embeddable_utils.mdx b/api_docs/kbn_lens_embeddable_utils.mdx index adf5e2c00daee..3c948e412acaf 100644 --- a/api_docs/kbn_lens_embeddable_utils.mdx +++ b/api_docs/kbn_lens_embeddable_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-embeddable-utils title: "@kbn/lens-embeddable-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-embeddable-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-embeddable-utils'] --- import kbnLensEmbeddableUtilsObj from './kbn_lens_embeddable_utils.devdocs.json'; diff --git a/api_docs/kbn_lens_formula_docs.mdx b/api_docs/kbn_lens_formula_docs.mdx index ee04a89d77981..4e44956f0ba0b 100644 --- a/api_docs/kbn_lens_formula_docs.mdx +++ b/api_docs/kbn_lens_formula_docs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-formula-docs title: "@kbn/lens-formula-docs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-formula-docs plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-formula-docs'] --- import kbnLensFormulaDocsObj from './kbn_lens_formula_docs.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 107cd2b9f3727..d43491eae3b84 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2024-10-29 +date: 2024-11-05 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 1bbcb9e0f66e0..cb6c14a364238 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_content_badge.mdx b/api_docs/kbn_managed_content_badge.mdx index 904d79261236f..d0a60e4fe9ac3 100644 --- a/api_docs/kbn_managed_content_badge.mdx +++ b/api_docs/kbn_managed_content_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-content-badge title: "@kbn/managed-content-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-content-badge plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-content-badge'] --- import kbnManagedContentBadgeObj from './kbn_managed_content_badge.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index 61e4e6d111d4c..9dfd7733881c3 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2024-10-29 +date: 2024-11-05 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 a3de13cc4b767..16bcc2e5dd33b 100644 --- a/api_docs/kbn_management_cards_navigation.mdx +++ b/api_docs/kbn_management_cards_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-cards-navigation title: "@kbn/management-cards-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-cards-navigation plugin -date: 2024-10-29 +date: 2024-11-05 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 68c73230fbc13..743240119df83 100644 --- a/api_docs/kbn_management_settings_application.mdx +++ b/api_docs/kbn_management_settings_application.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-application title: "@kbn/management-settings-application" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-application plugin -date: 2024-10-29 +date: 2024-11-05 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 5d8ab8c26bbbc..9f90b3cd5eb3f 100644 --- a/api_docs/kbn_management_settings_components_field_category.mdx +++ b/api_docs/kbn_management_settings_components_field_category.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-category title: "@kbn/management-settings-components-field-category" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-category plugin -date: 2024-10-29 +date: 2024-11-05 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 2df290a82748f..88931144ff307 100644 --- a/api_docs/kbn_management_settings_components_field_input.mdx +++ b/api_docs/kbn_management_settings_components_field_input.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-input title: "@kbn/management-settings-components-field-input" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-input plugin -date: 2024-10-29 +date: 2024-11-05 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 cdb37edddb3e9..e0581c08ca15b 100644 --- a/api_docs/kbn_management_settings_components_field_row.mdx +++ b/api_docs/kbn_management_settings_components_field_row.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-row title: "@kbn/management-settings-components-field-row" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-row plugin -date: 2024-10-29 +date: 2024-11-05 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 2bdb17e7a4c3e..4bf5378ce879a 100644 --- a/api_docs/kbn_management_settings_components_form.mdx +++ b/api_docs/kbn_management_settings_components_form.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-form title: "@kbn/management-settings-components-form" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-form plugin -date: 2024-10-29 +date: 2024-11-05 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 bbed2ff1714e1..7d8949fbf19a6 100644 --- a/api_docs/kbn_management_settings_field_definition.mdx +++ b/api_docs/kbn_management_settings_field_definition.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-field-definition title: "@kbn/management-settings-field-definition" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-field-definition plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-field-definition'] --- import kbnManagementSettingsFieldDefinitionObj from './kbn_management_settings_field_definition.devdocs.json'; diff --git a/api_docs/kbn_management_settings_ids.devdocs.json b/api_docs/kbn_management_settings_ids.devdocs.json index 068892a8dc375..19accbeb0d254 100644 --- a/api_docs/kbn_management_settings_ids.devdocs.json +++ b/api_docs/kbn_management_settings_ids.devdocs.json @@ -532,21 +532,6 @@ "trackAdoption": false, "initialIsOpen": false }, - { - "parentPluginId": "@kbn/management-settings-ids", - "id": "def-common.DISCOVER_SHOW_LEGACY_FIELD_TOP_VALUES_ID", - "type": "string", - "tags": [], - "label": "DISCOVER_SHOW_LEGACY_FIELD_TOP_VALUES_ID", - "description": [], - "signature": [ - "\"discover:showLegacyFieldTopValues\"" - ], - "path": "packages/kbn-management/settings/setting_ids/index.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, { "parentPluginId": "@kbn/management-settings-ids", "id": "def-common.DISCOVER_SHOW_MULTI_FIELDS_ID", diff --git a/api_docs/kbn_management_settings_ids.mdx b/api_docs/kbn_management_settings_ids.mdx index 3ea5165976a66..ce49939c667ca 100644 --- a/api_docs/kbn_management_settings_ids.mdx +++ b/api_docs/kbn_management_settings_ids.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-ids title: "@kbn/management-settings-ids" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-ids plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-ids'] --- import kbnManagementSettingsIdsObj from './kbn_management_settings_ids.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/ | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 141 | 0 | 140 | 0 | +| 140 | 0 | 139 | 0 | ## Common diff --git a/api_docs/kbn_management_settings_section_registry.mdx b/api_docs/kbn_management_settings_section_registry.mdx index 38bcd2cc9c383..cdcbcfbe74cd1 100644 --- a/api_docs/kbn_management_settings_section_registry.mdx +++ b/api_docs/kbn_management_settings_section_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-section-registry title: "@kbn/management-settings-section-registry" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-section-registry plugin -date: 2024-10-29 +date: 2024-11-05 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 5ed1e06adc4da..f70c108dad146 100644 --- a/api_docs/kbn_management_settings_types.mdx +++ b/api_docs/kbn_management_settings_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-types title: "@kbn/management-settings-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-types plugin -date: 2024-10-29 +date: 2024-11-05 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 0221f5ad298ee..5397decd1e5b8 100644 --- a/api_docs/kbn_management_settings_utilities.mdx +++ b/api_docs/kbn_management_settings_utilities.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-utilities title: "@kbn/management-settings-utilities" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-utilities plugin -date: 2024-10-29 +date: 2024-11-05 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 c8a379debc461..6d6d688b0feb5 100644 --- a/api_docs/kbn_management_storybook_config.mdx +++ b/api_docs/kbn_management_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-storybook-config title: "@kbn/management-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-storybook-config plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-storybook-config'] --- import kbnManagementStorybookConfigObj from './kbn_management_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_manifest.mdx b/api_docs/kbn_manifest.mdx index 81beee7925706..ac79087e64a56 100644 --- a/api_docs/kbn_manifest.mdx +++ b/api_docs/kbn_manifest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-manifest title: "@kbn/manifest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/manifest plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/manifest'] --- import kbnManifestObj from './kbn_manifest.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index 786771589ea27..d53547152f2b3 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2024-10-29 +date: 2024-11-05 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 62a2b89ef583a..ac01cb9a707ca 100644 --- a/api_docs/kbn_maps_vector_tile_utils.mdx +++ b/api_docs/kbn_maps_vector_tile_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-maps-vector-tile-utils title: "@kbn/maps-vector-tile-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/maps-vector-tile-utils plugin -date: 2024-10-29 +date: 2024-11-05 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 7493b8f074c85..c88164faa08db 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2024-10-29 +date: 2024-11-05 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 b3b1902843fe5..c61a6d9396b6f 100644 --- a/api_docs/kbn_ml_anomaly_utils.mdx +++ b/api_docs/kbn_ml_anomaly_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-anomaly-utils title: "@kbn/ml-anomaly-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-anomaly-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-anomaly-utils'] --- import kbnMlAnomalyUtilsObj from './kbn_ml_anomaly_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_cancellable_search.mdx b/api_docs/kbn_ml_cancellable_search.mdx index eb266a8c5afcc..41219d35a51eb 100644 --- a/api_docs/kbn_ml_cancellable_search.mdx +++ b/api_docs/kbn_ml_cancellable_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-cancellable-search title: "@kbn/ml-cancellable-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-cancellable-search plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-cancellable-search'] --- import kbnMlCancellableSearchObj from './kbn_ml_cancellable_search.devdocs.json'; diff --git a/api_docs/kbn_ml_category_validator.mdx b/api_docs/kbn_ml_category_validator.mdx index 0256b8d5b3dbd..b0e26cbe61c76 100644 --- a/api_docs/kbn_ml_category_validator.mdx +++ b/api_docs/kbn_ml_category_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-category-validator title: "@kbn/ml-category-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-category-validator plugin -date: 2024-10-29 +date: 2024-11-05 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 78a635f49cddb..d521c3dfc3af3 100644 --- a/api_docs/kbn_ml_chi2test.mdx +++ b/api_docs/kbn_ml_chi2test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-chi2test title: "@kbn/ml-chi2test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-chi2test plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-chi2test'] --- import kbnMlChi2testObj from './kbn_ml_chi2test.devdocs.json'; diff --git a/api_docs/kbn_ml_data_frame_analytics_utils.mdx b/api_docs/kbn_ml_data_frame_analytics_utils.mdx index 349ce3d9d1842..c9737443e8552 100644 --- a/api_docs/kbn_ml_data_frame_analytics_utils.mdx +++ b/api_docs/kbn_ml_data_frame_analytics_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-frame-analytics-utils title: "@kbn/ml-data-frame-analytics-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-frame-analytics-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-frame-analytics-utils'] --- import kbnMlDataFrameAnalyticsUtilsObj from './kbn_ml_data_frame_analytics_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_data_grid.mdx b/api_docs/kbn_ml_data_grid.mdx index f034b0c48b16d..df850cb6dc8b6 100644 --- a/api_docs/kbn_ml_data_grid.mdx +++ b/api_docs/kbn_ml_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-grid title: "@kbn/ml-data-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-grid plugin -date: 2024-10-29 +date: 2024-11-05 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 0b3efc1290247..b027eb8883409 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2024-10-29 +date: 2024-11-05 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 a60fdc4d9d7a5..48009b5fe5cdd 100644 --- a/api_docs/kbn_ml_date_utils.mdx +++ b/api_docs/kbn_ml_date_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-utils title: "@kbn/ml-date-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-utils plugin -date: 2024-10-29 +date: 2024-11-05 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 a9a6f376c8a45..e2c010f6fb55a 100644 --- a/api_docs/kbn_ml_error_utils.mdx +++ b/api_docs/kbn_ml_error_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-error-utils title: "@kbn/ml-error-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-error-utils plugin -date: 2024-10-29 +date: 2024-11-05 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_field_stats_flyout.mdx b/api_docs/kbn_ml_field_stats_flyout.mdx index 470340bfcfb78..f953a09822f1f 100644 --- a/api_docs/kbn_ml_field_stats_flyout.mdx +++ b/api_docs/kbn_ml_field_stats_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-field-stats-flyout title: "@kbn/ml-field-stats-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-field-stats-flyout plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-field-stats-flyout'] --- import kbnMlFieldStatsFlyoutObj from './kbn_ml_field_stats_flyout.devdocs.json'; diff --git a/api_docs/kbn_ml_in_memory_table.mdx b/api_docs/kbn_ml_in_memory_table.mdx index 3d1f6d15f40e6..add7e0eb480d1 100644 --- a/api_docs/kbn_ml_in_memory_table.mdx +++ b/api_docs/kbn_ml_in_memory_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-in-memory-table title: "@kbn/ml-in-memory-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-in-memory-table plugin -date: 2024-10-29 +date: 2024-11-05 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 d5cb955b646ad..66bf390b25d9d 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2024-10-29 +date: 2024-11-05 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 af269cc653a43..5cc4ab63dad89 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2024-10-29 +date: 2024-11-05 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 52464f1b5ce14..95061301abaf9 100644 --- a/api_docs/kbn_ml_kibana_theme.mdx +++ b/api_docs/kbn_ml_kibana_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-kibana-theme title: "@kbn/ml-kibana-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-kibana-theme plugin -date: 2024-10-29 +date: 2024-11-05 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 299038fce2258..fe97e9b147f59 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2024-10-29 +date: 2024-11-05 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 dc497b8065f6e..3e1557f46b02a 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2024-10-29 +date: 2024-11-05 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 df34a4eddc2be..163ced1f156e1 100644 --- a/api_docs/kbn_ml_number_utils.mdx +++ b/api_docs/kbn_ml_number_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-number-utils title: "@kbn/ml-number-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-number-utils plugin -date: 2024-10-29 +date: 2024-11-05 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_parse_interval.mdx b/api_docs/kbn_ml_parse_interval.mdx index 9b61a2cd9c910..342fd54cdbace 100644 --- a/api_docs/kbn_ml_parse_interval.mdx +++ b/api_docs/kbn_ml_parse_interval.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-parse-interval title: "@kbn/ml-parse-interval" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-parse-interval plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-parse-interval'] --- import kbnMlParseIntervalObj from './kbn_ml_parse_interval.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index b98f98ab662fa..24fa080254054 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2024-10-29 +date: 2024-11-05 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 07e3c136d330a..3a3315719390d 100644 --- a/api_docs/kbn_ml_random_sampler_utils.mdx +++ b/api_docs/kbn_ml_random_sampler_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-random-sampler-utils title: "@kbn/ml-random-sampler-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-random-sampler-utils plugin -date: 2024-10-29 +date: 2024-11-05 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 080c87f952a05..5fc3b143b830a 100644 --- a/api_docs/kbn_ml_route_utils.mdx +++ b/api_docs/kbn_ml_route_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-route-utils title: "@kbn/ml-route-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-route-utils plugin -date: 2024-10-29 +date: 2024-11-05 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 19b53e2278bf6..03885b113d560 100644 --- a/api_docs/kbn_ml_runtime_field_utils.mdx +++ b/api_docs/kbn_ml_runtime_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-runtime-field-utils title: "@kbn/ml-runtime-field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-runtime-field-utils plugin -date: 2024-10-29 +date: 2024-11-05 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 9a3354a802f82..1e1584f495dde 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_time_buckets.mdx b/api_docs/kbn_ml_time_buckets.mdx index 26ecb786ee295..3ad34b6eced4a 100644 --- a/api_docs/kbn_ml_time_buckets.mdx +++ b/api_docs/kbn_ml_time_buckets.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-time-buckets title: "@kbn/ml-time-buckets" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-time-buckets plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-time-buckets'] --- import kbnMlTimeBucketsObj from './kbn_ml_time_buckets.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index 7fb38f79881de..281becf363b47 100644 --- a/api_docs/kbn_ml_trained_models_utils.mdx +++ b/api_docs/kbn_ml_trained_models_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-trained-models-utils title: "@kbn/ml-trained-models-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-trained-models-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-trained-models-utils'] --- import kbnMlTrainedModelsUtilsObj from './kbn_ml_trained_models_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_ui_actions.mdx b/api_docs/kbn_ml_ui_actions.mdx index 29df056a7fb92..9625e75e12c06 100644 --- a/api_docs/kbn_ml_ui_actions.mdx +++ b/api_docs/kbn_ml_ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-ui-actions title: "@kbn/ml-ui-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-ui-actions plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-ui-actions'] --- import kbnMlUiActionsObj from './kbn_ml_ui_actions.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index aa84b80c01ab0..77fce539ca910 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_ml_validators.mdx b/api_docs/kbn_ml_validators.mdx index d22dc9e026e9a..b135bb7a4237d 100644 --- a/api_docs/kbn_ml_validators.mdx +++ b/api_docs/kbn_ml_validators.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-validators title: "@kbn/ml-validators" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-validators plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-validators'] --- import kbnMlValidatorsObj from './kbn_ml_validators.devdocs.json'; diff --git a/api_docs/kbn_mock_idp_utils.mdx b/api_docs/kbn_mock_idp_utils.mdx index b193c9bee5c47..d66d7881f7da4 100644 --- a/api_docs/kbn_mock_idp_utils.mdx +++ b/api_docs/kbn_mock_idp_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mock-idp-utils title: "@kbn/mock-idp-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mock-idp-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mock-idp-utils'] --- import kbnMockIdpUtilsObj from './kbn_mock_idp_utils.devdocs.json'; diff --git a/api_docs/kbn_monaco.devdocs.json b/api_docs/kbn_monaco.devdocs.json index 4a460e13cec3c..fcb876065c8a2 100644 --- a/api_docs/kbn_monaco.devdocs.json +++ b/api_docs/kbn_monaco.devdocs.json @@ -509,10 +509,10 @@ }, { "parentPluginId": "@kbn/monaco", - "id": "def-common.ESQLCallbacks.getFieldsFor", + "id": "def-common.ESQLCallbacks.getColumnsFor", "type": "Function", "tags": [], - "label": "getFieldsFor", + "label": "getColumnsFor", "description": [], "signature": [ "CallbackFn<{ query: string; }, ", diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 2c59f56d79f26..d04f198f14464 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2024-10-29 +date: 2024-11-05 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 04c83fc5bbdfb..2d1b7030a01ee 100644 --- a/api_docs/kbn_object_versioning.mdx +++ b/api_docs/kbn_object_versioning.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning title: "@kbn/object-versioning" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning'] --- import kbnObjectVersioningObj from './kbn_object_versioning.devdocs.json'; diff --git a/api_docs/kbn_object_versioning_utils.mdx b/api_docs/kbn_object_versioning_utils.mdx index 41323be27e90e..113de46fe1329 100644 --- a/api_docs/kbn_object_versioning_utils.mdx +++ b/api_docs/kbn_object_versioning_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning-utils title: "@kbn/object-versioning-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning-utils'] --- import kbnObjectVersioningUtilsObj from './kbn_object_versioning_utils.devdocs.json'; diff --git a/api_docs/kbn_observability_alert_details.mdx b/api_docs/kbn_observability_alert_details.mdx index fe0dcd5944146..f76504a941c9e 100644 --- a/api_docs/kbn_observability_alert_details.mdx +++ b/api_docs/kbn_observability_alert_details.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alert-details title: "@kbn/observability-alert-details" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alert-details plugin -date: 2024-10-29 +date: 2024-11-05 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_rule_utils.mdx b/api_docs/kbn_observability_alerting_rule_utils.mdx index f9696c490322c..0d2a46bc2b65e 100644 --- a/api_docs/kbn_observability_alerting_rule_utils.mdx +++ b/api_docs/kbn_observability_alerting_rule_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alerting-rule-utils title: "@kbn/observability-alerting-rule-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alerting-rule-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alerting-rule-utils'] --- import kbnObservabilityAlertingRuleUtilsObj from './kbn_observability_alerting_rule_utils.devdocs.json'; diff --git a/api_docs/kbn_observability_alerting_test_data.mdx b/api_docs/kbn_observability_alerting_test_data.mdx index 40e67ee5af980..b8dfa4e440b2e 100644 --- a/api_docs/kbn_observability_alerting_test_data.mdx +++ b/api_docs/kbn_observability_alerting_test_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alerting-test-data title: "@kbn/observability-alerting-test-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alerting-test-data plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alerting-test-data'] --- import kbnObservabilityAlertingTestDataObj from './kbn_observability_alerting_test_data.devdocs.json'; diff --git a/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx b/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx index 4e844a8ccfdaa..15ae84656d068 100644 --- a/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx +++ b/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-get-padded-alert-time-range-util title: "@kbn/observability-get-padded-alert-time-range-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-get-padded-alert-time-range-util plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-get-padded-alert-time-range-util'] --- import kbnObservabilityGetPaddedAlertTimeRangeUtilObj from './kbn_observability_get_padded_alert_time_range_util.devdocs.json'; diff --git a/api_docs/kbn_observability_logs_overview.mdx b/api_docs/kbn_observability_logs_overview.mdx index 8e23ff06e78b4..9c197e418ff57 100644 --- a/api_docs/kbn_observability_logs_overview.mdx +++ b/api_docs/kbn_observability_logs_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-logs-overview title: "@kbn/observability-logs-overview" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-logs-overview plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-logs-overview'] --- import kbnObservabilityLogsOverviewObj from './kbn_observability_logs_overview.devdocs.json'; diff --git a/api_docs/kbn_observability_synthetics_test_data.mdx b/api_docs/kbn_observability_synthetics_test_data.mdx index 8c23a8d033605..05d7ac32fae91 100644 --- a/api_docs/kbn_observability_synthetics_test_data.mdx +++ b/api_docs/kbn_observability_synthetics_test_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-synthetics-test-data title: "@kbn/observability-synthetics-test-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-synthetics-test-data plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-synthetics-test-data'] --- import kbnObservabilitySyntheticsTestDataObj from './kbn_observability_synthetics_test_data.devdocs.json'; diff --git a/api_docs/kbn_openapi_bundler.mdx b/api_docs/kbn_openapi_bundler.mdx index 964c1c90898b9..fb2d697cd597c 100644 --- a/api_docs/kbn_openapi_bundler.mdx +++ b/api_docs/kbn_openapi_bundler.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-bundler title: "@kbn/openapi-bundler" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-bundler plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-bundler'] --- import kbnOpenapiBundlerObj from './kbn_openapi_bundler.devdocs.json'; diff --git a/api_docs/kbn_openapi_generator.mdx b/api_docs/kbn_openapi_generator.mdx index e177b09760d8b..00c526b723a81 100644 --- a/api_docs/kbn_openapi_generator.mdx +++ b/api_docs/kbn_openapi_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-generator title: "@kbn/openapi-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-generator plugin -date: 2024-10-29 +date: 2024-11-05 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 d461e6e883a3a..0b5cc24cbc725 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2024-10-29 +date: 2024-11-05 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 b7f9911d142df..99427ef1f7db7 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2024-10-29 +date: 2024-11-05 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 c5d6ea3092998..4f13ef20fe716 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_panel_loader.mdx b/api_docs/kbn_panel_loader.mdx index 799b4e563753d..69889c9d2080b 100644 --- a/api_docs/kbn_panel_loader.mdx +++ b/api_docs/kbn_panel_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-panel-loader title: "@kbn/panel-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/panel-loader plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/panel-loader'] --- import kbnPanelLoaderObj from './kbn_panel_loader.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index f44e71260edf7..eafc733ccfef1 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_check.mdx b/api_docs/kbn_plugin_check.mdx index 58c279e265a30..6cc704aa52b6c 100644 --- a/api_docs/kbn_plugin_check.mdx +++ b/api_docs/kbn_plugin_check.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-check title: "@kbn/plugin-check" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-check plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-check'] --- import kbnPluginCheckObj from './kbn_plugin_check.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index ff4207acb1e13..63e756b19cba0 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2024-10-29 +date: 2024-11-05 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 fbcbe71f5e6ef..323ef1ffa0396 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_presentation_containers.mdx b/api_docs/kbn_presentation_containers.mdx index f873b523ac5a5..f447e3ae3aac6 100644 --- a/api_docs/kbn_presentation_containers.mdx +++ b/api_docs/kbn_presentation_containers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-containers title: "@kbn/presentation-containers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-containers plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-containers'] --- import kbnPresentationContainersObj from './kbn_presentation_containers.devdocs.json'; diff --git a/api_docs/kbn_presentation_publishing.mdx b/api_docs/kbn_presentation_publishing.mdx index 2a8c2dcee0a75..e0f3d80b96aea 100644 --- a/api_docs/kbn_presentation_publishing.mdx +++ b/api_docs/kbn_presentation_publishing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-publishing title: "@kbn/presentation-publishing" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-publishing plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-publishing'] --- import kbnPresentationPublishingObj from './kbn_presentation_publishing.devdocs.json'; diff --git a/api_docs/kbn_product_doc_artifact_builder.mdx b/api_docs/kbn_product_doc_artifact_builder.mdx index 6d89764bfbeaf..1488fe46e0925 100644 --- a/api_docs/kbn_product_doc_artifact_builder.mdx +++ b/api_docs/kbn_product_doc_artifact_builder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-product-doc-artifact-builder title: "@kbn/product-doc-artifact-builder" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/product-doc-artifact-builder plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/product-doc-artifact-builder'] --- import kbnProductDocArtifactBuilderObj from './kbn_product_doc_artifact_builder.devdocs.json'; diff --git a/api_docs/kbn_profiling_utils.mdx b/api_docs/kbn_profiling_utils.mdx index f3efb060e8567..f0ba3e0c61955 100644 --- a/api_docs/kbn_profiling_utils.mdx +++ b/api_docs/kbn_profiling_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-profiling-utils title: "@kbn/profiling-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/profiling-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/profiling-utils'] --- import kbnProfilingUtilsObj from './kbn_profiling_utils.devdocs.json'; diff --git a/api_docs/kbn_random_sampling.mdx b/api_docs/kbn_random_sampling.mdx index 16d90b33f6b0b..d634cac4e99fe 100644 --- a/api_docs/kbn_random_sampling.mdx +++ b/api_docs/kbn_random_sampling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-random-sampling title: "@kbn/random-sampling" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/random-sampling plugin -date: 2024-10-29 +date: 2024-11-05 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 7af812ab24161..d8522a40b35f8 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_react_hooks.mdx b/api_docs/kbn_react_hooks.mdx index 58e5111bf0f94..f649de8d93608 100644 --- a/api_docs/kbn_react_hooks.mdx +++ b/api_docs/kbn_react_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-hooks title: "@kbn/react-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-hooks plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-hooks'] --- import kbnReactHooksObj from './kbn_react_hooks.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_common.mdx b/api_docs/kbn_react_kibana_context_common.mdx index 7bb961acca4eb..8c25c7c530558 100644 --- a/api_docs/kbn_react_kibana_context_common.mdx +++ b/api_docs/kbn_react_kibana_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-common title: "@kbn/react-kibana-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-common plugin -date: 2024-10-29 +date: 2024-11-05 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 6270aab96b1ff..f454aaef242dc 100644 --- a/api_docs/kbn_react_kibana_context_render.mdx +++ b/api_docs/kbn_react_kibana_context_render.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-render title: "@kbn/react-kibana-context-render" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-render plugin -date: 2024-10-29 +date: 2024-11-05 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 b88dd4eb54f0c..0954404eae12d 100644 --- a/api_docs/kbn_react_kibana_context_root.mdx +++ b/api_docs/kbn_react_kibana_context_root.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-root title: "@kbn/react-kibana-context-root" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-root plugin -date: 2024-10-29 +date: 2024-11-05 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 78310f89f7951..5fa52a54866e0 100644 --- a/api_docs/kbn_react_kibana_context_styled.mdx +++ b/api_docs/kbn_react_kibana_context_styled.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-styled title: "@kbn/react-kibana-context-styled" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-styled plugin -date: 2024-10-29 +date: 2024-11-05 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 cfaa730719d4e..89910ee7796f9 100644 --- a/api_docs/kbn_react_kibana_context_theme.mdx +++ b/api_docs/kbn_react_kibana_context_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-theme title: "@kbn/react-kibana-context-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-theme plugin -date: 2024-10-29 +date: 2024-11-05 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 979dd494acffe..2c9306ef75cbf 100644 --- a/api_docs/kbn_react_kibana_mount.mdx +++ b/api_docs/kbn_react_kibana_mount.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-mount title: "@kbn/react-kibana-mount" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-mount plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-mount'] --- import kbnReactKibanaMountObj from './kbn_react_kibana_mount.devdocs.json'; diff --git a/api_docs/kbn_recently_accessed.mdx b/api_docs/kbn_recently_accessed.mdx index a01060c33d61d..ee289e1cd58c4 100644 --- a/api_docs/kbn_recently_accessed.mdx +++ b/api_docs/kbn_recently_accessed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-recently-accessed title: "@kbn/recently-accessed" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/recently-accessed plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/recently-accessed'] --- import kbnRecentlyAccessedObj from './kbn_recently_accessed.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index 8e8600ae16bd2..7331260b401fe 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2024-10-29 +date: 2024-11-05 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 d8f90ab3aaa79..7ec7785a6effb 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2024-10-29 +date: 2024-11-05 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 b3a255ad3e045..a9747a9b0e572 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2024-10-29 +date: 2024-11-05 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 7c9dbb2c4741b..88ac7ca83d31b 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2024-10-29 +date: 2024-11-05 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 be668e07d7a70..b25f9dbd19812 100644 --- a/api_docs/kbn_reporting_common.mdx +++ b/api_docs/kbn_reporting_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-common title: "@kbn/reporting-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-common plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_csv_share_panel.mdx b/api_docs/kbn_reporting_csv_share_panel.mdx index 5cf72293160bf..deafd6eca3410 100644 --- a/api_docs/kbn_reporting_csv_share_panel.mdx +++ b/api_docs/kbn_reporting_csv_share_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-csv-share-panel title: "@kbn/reporting-csv-share-panel" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-csv-share-panel plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-csv-share-panel'] --- import kbnReportingCsvSharePanelObj from './kbn_reporting_csv_share_panel.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv.mdx b/api_docs/kbn_reporting_export_types_csv.mdx index af3604b5570fc..b7d392f58e92b 100644 --- a/api_docs/kbn_reporting_export_types_csv.mdx +++ b/api_docs/kbn_reporting_export_types_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv title: "@kbn/reporting-export-types-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv plugin -date: 2024-10-29 +date: 2024-11-05 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 5a402a817a24d..a39250899e514 100644 --- a/api_docs/kbn_reporting_export_types_csv_common.mdx +++ b/api_docs/kbn_reporting_export_types_csv_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv-common title: "@kbn/reporting-export-types-csv-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv-common plugin -date: 2024-10-29 +date: 2024-11-05 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 e9a87020dc8d5..7aea183f13a21 100644 --- a/api_docs/kbn_reporting_export_types_pdf.mdx +++ b/api_docs/kbn_reporting_export_types_pdf.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf title: "@kbn/reporting-export-types-pdf" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf plugin -date: 2024-10-29 +date: 2024-11-05 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 36f200ad4b1a0..680d5fa288a7a 100644 --- a/api_docs/kbn_reporting_export_types_pdf_common.mdx +++ b/api_docs/kbn_reporting_export_types_pdf_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf-common title: "@kbn/reporting-export-types-pdf-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf-common plugin -date: 2024-10-29 +date: 2024-11-05 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 f79e6204bf6c3..e1f7c3e6c8eef 100644 --- a/api_docs/kbn_reporting_export_types_png.mdx +++ b/api_docs/kbn_reporting_export_types_png.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png title: "@kbn/reporting-export-types-png" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png plugin -date: 2024-10-29 +date: 2024-11-05 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 c2d78cc9e5165..773d60b965773 100644 --- a/api_docs/kbn_reporting_export_types_png_common.mdx +++ b/api_docs/kbn_reporting_export_types_png_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png-common title: "@kbn/reporting-export-types-png-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png-common plugin -date: 2024-10-29 +date: 2024-11-05 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 8e76405d29abe..1f5f2d4204682 100644 --- a/api_docs/kbn_reporting_mocks_server.mdx +++ b/api_docs/kbn_reporting_mocks_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-mocks-server title: "@kbn/reporting-mocks-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-mocks-server plugin -date: 2024-10-29 +date: 2024-11-05 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 6bd5f5e14e820..58b7c8e1421e5 100644 --- a/api_docs/kbn_reporting_public.mdx +++ b/api_docs/kbn_reporting_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-public title: "@kbn/reporting-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-public plugin -date: 2024-10-29 +date: 2024-11-05 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 44a14fb771ede..048dc326cfa47 100644 --- a/api_docs/kbn_reporting_server.mdx +++ b/api_docs/kbn_reporting_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-server title: "@kbn/reporting-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-server plugin -date: 2024-10-29 +date: 2024-11-05 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 5fc7535667618..9eca84bc13bb9 100644 --- a/api_docs/kbn_resizable_layout.mdx +++ b/api_docs/kbn_resizable_layout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-resizable-layout title: "@kbn/resizable-layout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/resizable-layout plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/resizable-layout'] --- import kbnResizableLayoutObj from './kbn_resizable_layout.devdocs.json'; diff --git a/api_docs/kbn_response_ops_feature_flag_service.mdx b/api_docs/kbn_response_ops_feature_flag_service.mdx index 98e710a3f3775..bf556bcce6137 100644 --- a/api_docs/kbn_response_ops_feature_flag_service.mdx +++ b/api_docs/kbn_response_ops_feature_flag_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-response-ops-feature-flag-service title: "@kbn/response-ops-feature-flag-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/response-ops-feature-flag-service plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/response-ops-feature-flag-service'] --- import kbnResponseOpsFeatureFlagServiceObj from './kbn_response_ops_feature_flag_service.devdocs.json'; diff --git a/api_docs/kbn_response_ops_rule_params.mdx b/api_docs/kbn_response_ops_rule_params.mdx index 0efeec9337ef0..59b8cdef48b02 100644 --- a/api_docs/kbn_response_ops_rule_params.mdx +++ b/api_docs/kbn_response_ops_rule_params.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-response-ops-rule-params title: "@kbn/response-ops-rule-params" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/response-ops-rule-params plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/response-ops-rule-params'] --- import kbnResponseOpsRuleParamsObj from './kbn_response_ops_rule_params.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index 30d78a88f4d20..3d89133516280 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_rollup.mdx b/api_docs/kbn_rollup.mdx index 1e5f5527865c6..b98a97774f4dc 100644 --- a/api_docs/kbn_rollup.mdx +++ b/api_docs/kbn_rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rollup title: "@kbn/rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rollup plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rollup'] --- import kbnRollupObj from './kbn_rollup.devdocs.json'; diff --git a/api_docs/kbn_router_to_openapispec.mdx b/api_docs/kbn_router_to_openapispec.mdx index aac78d2f4ea77..de316c4feca18 100644 --- a/api_docs/kbn_router_to_openapispec.mdx +++ b/api_docs/kbn_router_to_openapispec.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-to-openapispec title: "@kbn/router-to-openapispec" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-to-openapispec plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-to-openapispec'] --- import kbnRouterToOpenapispecObj from './kbn_router_to_openapispec.devdocs.json'; diff --git a/api_docs/kbn_router_utils.mdx b/api_docs/kbn_router_utils.mdx index 7b9f0bf9a8122..80ad761794f40 100644 --- a/api_docs/kbn_router_utils.mdx +++ b/api_docs/kbn_router_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-utils title: "@kbn/router-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-utils'] --- import kbnRouterUtilsObj from './kbn_router_utils.devdocs.json'; diff --git a/api_docs/kbn_rrule.mdx b/api_docs/kbn_rrule.mdx index e4687938403f1..577425e07bda8 100644 --- a/api_docs/kbn_rrule.mdx +++ b/api_docs/kbn_rrule.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rrule title: "@kbn/rrule" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rrule plugin -date: 2024-10-29 +date: 2024-11-05 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 d4cc44103a83a..82146a777a392 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2024-10-29 +date: 2024-11-05 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 ef655270d104f..662123daa7c2b 100644 --- a/api_docs/kbn_saved_objects_settings.mdx +++ b/api_docs/kbn_saved_objects_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-saved-objects-settings title: "@kbn/saved-objects-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/saved-objects-settings plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_screenshotting_server.mdx b/api_docs/kbn_screenshotting_server.mdx index ea61b9b3077e1..5caf1794708c0 100644 --- a/api_docs/kbn_screenshotting_server.mdx +++ b/api_docs/kbn_screenshotting_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-screenshotting-server title: "@kbn/screenshotting-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/screenshotting-server plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/screenshotting-server'] --- import kbnScreenshottingServerObj from './kbn_screenshotting_server.devdocs.json'; diff --git a/api_docs/kbn_search_api_keys_components.mdx b/api_docs/kbn_search_api_keys_components.mdx index 540d30ea3862c..c76c3cfd671dd 100644 --- a/api_docs/kbn_search_api_keys_components.mdx +++ b/api_docs/kbn_search_api_keys_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-keys-components title: "@kbn/search-api-keys-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-keys-components plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-keys-components'] --- import kbnSearchApiKeysComponentsObj from './kbn_search_api_keys_components.devdocs.json'; diff --git a/api_docs/kbn_search_api_keys_server.mdx b/api_docs/kbn_search_api_keys_server.mdx index 0c1997ae3da90..f87ac1ac5d3f9 100644 --- a/api_docs/kbn_search_api_keys_server.mdx +++ b/api_docs/kbn_search_api_keys_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-keys-server title: "@kbn/search-api-keys-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-keys-server plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-keys-server'] --- import kbnSearchApiKeysServerObj from './kbn_search_api_keys_server.devdocs.json'; diff --git a/api_docs/kbn_search_api_panels.mdx b/api_docs/kbn_search_api_panels.mdx index ba7769c70acdb..42eff9498c7d8 100644 --- a/api_docs/kbn_search_api_panels.mdx +++ b/api_docs/kbn_search_api_panels.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-panels title: "@kbn/search-api-panels" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-panels plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-panels'] --- import kbnSearchApiPanelsObj from './kbn_search_api_panels.devdocs.json'; diff --git a/api_docs/kbn_search_connectors.mdx b/api_docs/kbn_search_connectors.mdx index b879e94b3167d..b00c090593e42 100644 --- a/api_docs/kbn_search_connectors.mdx +++ b/api_docs/kbn_search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-connectors title: "@kbn/search-connectors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-connectors plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-connectors'] --- import kbnSearchConnectorsObj from './kbn_search_connectors.devdocs.json'; diff --git a/api_docs/kbn_search_errors.mdx b/api_docs/kbn_search_errors.mdx index c073a094c3233..958d395bca64e 100644 --- a/api_docs/kbn_search_errors.mdx +++ b/api_docs/kbn_search_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-errors title: "@kbn/search-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-errors plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-errors'] --- import kbnSearchErrorsObj from './kbn_search_errors.devdocs.json'; diff --git a/api_docs/kbn_search_index_documents.mdx b/api_docs/kbn_search_index_documents.mdx index ec316bdbecb63..2709014d56215 100644 --- a/api_docs/kbn_search_index_documents.mdx +++ b/api_docs/kbn_search_index_documents.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-index-documents title: "@kbn/search-index-documents" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-index-documents plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-index-documents'] --- import kbnSearchIndexDocumentsObj from './kbn_search_index_documents.devdocs.json'; diff --git a/api_docs/kbn_search_response_warnings.mdx b/api_docs/kbn_search_response_warnings.mdx index de23440acbe1b..070b8045c09fa 100644 --- a/api_docs/kbn_search_response_warnings.mdx +++ b/api_docs/kbn_search_response_warnings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-response-warnings title: "@kbn/search-response-warnings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-response-warnings plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-response-warnings'] --- import kbnSearchResponseWarningsObj from './kbn_search_response_warnings.devdocs.json'; diff --git a/api_docs/kbn_search_shared_ui.mdx b/api_docs/kbn_search_shared_ui.mdx index e29708569c1ca..637c17a6f4243 100644 --- a/api_docs/kbn_search_shared_ui.mdx +++ b/api_docs/kbn_search_shared_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-shared-ui title: "@kbn/search-shared-ui" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-shared-ui plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-shared-ui'] --- import kbnSearchSharedUiObj from './kbn_search_shared_ui.devdocs.json'; diff --git a/api_docs/kbn_search_types.mdx b/api_docs/kbn_search_types.mdx index 08a57a0760293..479347c00da88 100644 --- a/api_docs/kbn_search_types.mdx +++ b/api_docs/kbn_search_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-types title: "@kbn/search-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-types plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-types'] --- import kbnSearchTypesObj from './kbn_search_types.devdocs.json'; diff --git a/api_docs/kbn_security_api_key_management.mdx b/api_docs/kbn_security_api_key_management.mdx index 39d1bf07a5773..049f8aceddd96 100644 --- a/api_docs/kbn_security_api_key_management.mdx +++ b/api_docs/kbn_security_api_key_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-api-key-management title: "@kbn/security-api-key-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-api-key-management plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-api-key-management'] --- import kbnSecurityApiKeyManagementObj from './kbn_security_api_key_management.devdocs.json'; diff --git a/api_docs/kbn_security_authorization_core.mdx b/api_docs/kbn_security_authorization_core.mdx index f7b92f00e421f..b3b971c4817f3 100644 --- a/api_docs/kbn_security_authorization_core.mdx +++ b/api_docs/kbn_security_authorization_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-authorization-core title: "@kbn/security-authorization-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-authorization-core plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-authorization-core'] --- import kbnSecurityAuthorizationCoreObj from './kbn_security_authorization_core.devdocs.json'; diff --git a/api_docs/kbn_security_authorization_core_common.mdx b/api_docs/kbn_security_authorization_core_common.mdx index 086b5b14f4a77..262eb832a38f9 100644 --- a/api_docs/kbn_security_authorization_core_common.mdx +++ b/api_docs/kbn_security_authorization_core_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-authorization-core-common title: "@kbn/security-authorization-core-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-authorization-core-common plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-authorization-core-common'] --- import kbnSecurityAuthorizationCoreCommonObj from './kbn_security_authorization_core_common.devdocs.json'; diff --git a/api_docs/kbn_security_form_components.mdx b/api_docs/kbn_security_form_components.mdx index ae2bb657b9388..353a92458c02e 100644 --- a/api_docs/kbn_security_form_components.mdx +++ b/api_docs/kbn_security_form_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-form-components title: "@kbn/security-form-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-form-components plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-form-components'] --- import kbnSecurityFormComponentsObj from './kbn_security_form_components.devdocs.json'; diff --git a/api_docs/kbn_security_hardening.mdx b/api_docs/kbn_security_hardening.mdx index ef6f143878b9a..026d20d5c4555 100644 --- a/api_docs/kbn_security_hardening.mdx +++ b/api_docs/kbn_security_hardening.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-hardening title: "@kbn/security-hardening" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-hardening plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-hardening'] --- import kbnSecurityHardeningObj from './kbn_security_hardening.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_common.mdx b/api_docs/kbn_security_plugin_types_common.mdx index 1665751a0cf7d..3548b21a9c206 100644 --- a/api_docs/kbn_security_plugin_types_common.mdx +++ b/api_docs/kbn_security_plugin_types_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-common title: "@kbn/security-plugin-types-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-common plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-common'] --- import kbnSecurityPluginTypesCommonObj from './kbn_security_plugin_types_common.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_public.mdx b/api_docs/kbn_security_plugin_types_public.mdx index b46c18505af15..235fc5b9f8bbb 100644 --- a/api_docs/kbn_security_plugin_types_public.mdx +++ b/api_docs/kbn_security_plugin_types_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-public title: "@kbn/security-plugin-types-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-public plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-public'] --- import kbnSecurityPluginTypesPublicObj from './kbn_security_plugin_types_public.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_server.mdx b/api_docs/kbn_security_plugin_types_server.mdx index b1d0c138304e8..2938e35c3ac70 100644 --- a/api_docs/kbn_security_plugin_types_server.mdx +++ b/api_docs/kbn_security_plugin_types_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-server title: "@kbn/security-plugin-types-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-server plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-server'] --- import kbnSecurityPluginTypesServerObj from './kbn_security_plugin_types_server.devdocs.json'; diff --git a/api_docs/kbn_security_role_management_model.mdx b/api_docs/kbn_security_role_management_model.mdx index 96c1fdb1479e4..79ac200947739 100644 --- a/api_docs/kbn_security_role_management_model.mdx +++ b/api_docs/kbn_security_role_management_model.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-role-management-model title: "@kbn/security-role-management-model" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-role-management-model plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-role-management-model'] --- import kbnSecurityRoleManagementModelObj from './kbn_security_role_management_model.devdocs.json'; diff --git a/api_docs/kbn_security_solution_common.mdx b/api_docs/kbn_security_solution_common.mdx index 77ac3403bb5b1..7931e25c2d239 100644 --- a/api_docs/kbn_security_solution_common.mdx +++ b/api_docs/kbn_security_solution_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-common title: "@kbn/security-solution-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-common plugin -date: 2024-10-29 +date: 2024-11-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-common'] --- import kbnSecuritySolutionCommonObj from './kbn_security_solution_common.devdocs.json'; diff --git a/api_docs/kbn_security_solution_distribution_bar.mdx b/api_docs/kbn_security_solution_distribution_bar.mdx index 84bbad961458b..ec2a244929f25 100644 --- a/api_docs/kbn_security_solution_distribution_bar.mdx +++ b/api_docs/kbn_security_solution_distribution_bar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-distribution-bar title: "@kbn/security-solution-distribution-bar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-distribution-bar plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-distribution-bar'] --- import kbnSecuritySolutionDistributionBarObj from './kbn_security_solution_distribution_bar.devdocs.json'; diff --git a/api_docs/kbn_security_solution_features.mdx b/api_docs/kbn_security_solution_features.mdx index f8717a8ab4505..1aa557c0443a9 100644 --- a/api_docs/kbn_security_solution_features.mdx +++ b/api_docs/kbn_security_solution_features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-features title: "@kbn/security-solution-features" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-features plugin -date: 2024-10-29 +date: 2024-11-05 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 1acbc11797c00..56d075e4a4859 100644 --- a/api_docs/kbn_security_solution_navigation.mdx +++ b/api_docs/kbn_security_solution_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-navigation title: "@kbn/security-solution-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-navigation plugin -date: 2024-10-29 +date: 2024-11-05 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 7755043d6ad7e..1d28cc108ffc2 100644 --- a/api_docs/kbn_security_solution_side_nav.mdx +++ b/api_docs/kbn_security_solution_side_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-side-nav title: "@kbn/security-solution-side-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-side-nav plugin -date: 2024-10-29 +date: 2024-11-05 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 d7014e68f587d..20121767b45ac 100644 --- a/api_docs/kbn_security_solution_storybook_config.mdx +++ b/api_docs/kbn_security_solution_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-storybook-config title: "@kbn/security-solution-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-storybook-config plugin -date: 2024-10-29 +date: 2024-11-05 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_security_ui_components.mdx b/api_docs/kbn_security_ui_components.mdx index 0f9167b7ee9f4..1e27e2ec85d05 100644 --- a/api_docs/kbn_security_ui_components.mdx +++ b/api_docs/kbn_security_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-ui-components title: "@kbn/security-ui-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-ui-components plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-ui-components'] --- import kbnSecurityUiComponentsObj from './kbn_security_ui_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 64c22fd6a2041..65bca33413073 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2024-10-29 +date: 2024-11-05 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 cbc39b3ca16c8..7d37434cfc2e8 100644 --- a/api_docs/kbn_securitysolution_data_table.mdx +++ b/api_docs/kbn_securitysolution_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-data-table title: "@kbn/securitysolution-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-data-table plugin -date: 2024-10-29 +date: 2024-11-05 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 7c9e0d14eda8a..8c67a746fc5c2 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2024-10-29 +date: 2024-11-05 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 748ad7c77374c..750e8229421e8 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] --- import kbnSecuritysolutionEsUtilsObj from './kbn_securitysolution_es_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_exception_list_components.mdx b/api_docs/kbn_securitysolution_exception_list_components.mdx index 4f4c0428d4b97..21ed2b891ad6b 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] --- import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index 79ed5e3018b61..22f78014667aa 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2024-10-29 +date: 2024-11-05 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 e611916c3686e..98cc1cf24daeb 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2024-10-29 +date: 2024-11-05 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 e15e1b441dad0..dbea27394a602 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2024-10-29 +date: 2024-11-05 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 fda0727968327..db3d19833911f 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2024-10-29 +date: 2024-11-05 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 662762d9e2f8f..32e29b4ca1190 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2024-10-29 +date: 2024-11-05 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 2ed39cbc256cd..2fe302ba8f292 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] --- import kbnSecuritysolutionListApiObj from './kbn_securitysolution_list_api.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index 52d48974b97bd..14f2e22393f55 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2024-10-29 +date: 2024-11-05 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 f87d1a4ff879f..1ace44e29f6a4 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2024-10-29 +date: 2024-11-05 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 90a42d400611c..63a0f0adfc5b2 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2024-10-29 +date: 2024-11-05 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 ec6de35f47f9f..31b647bd498c5 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2024-10-29 +date: 2024-11-05 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 46998b2be3afe..e042da773abf4 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid'] --- import kbnSecuritysolutionTGridObj from './kbn_securitysolution_t_grid.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index 69075506cb0fc..b87898a414710 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] --- import kbnSecuritysolutionUtilsObj from './kbn_securitysolution_utils.devdocs.json'; diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index 1944dfb444104..c07a50b6ffe46 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools'] --- import kbnServerHttpToolsObj from './kbn_server_http_tools.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx index dc6f7ca818d5f..d74bd961d8158 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository_client.mdx b/api_docs/kbn_server_route_repository_client.mdx index d0c7f5a07756c..c269680c12df6 100644 --- a/api_docs/kbn_server_route_repository_client.mdx +++ b/api_docs/kbn_server_route_repository_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository-client title: "@kbn/server-route-repository-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository-client plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository-client'] --- import kbnServerRouteRepositoryClientObj from './kbn_server_route_repository_client.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository_utils.mdx b/api_docs/kbn_server_route_repository_utils.mdx index f505a2fb43fff..6bc875810a386 100644 --- a/api_docs/kbn_server_route_repository_utils.mdx +++ b/api_docs/kbn_server_route_repository_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository-utils title: "@kbn/server-route-repository-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository-utils'] --- import kbnServerRouteRepositoryUtilsObj from './kbn_server_route_repository_utils.devdocs.json'; diff --git a/api_docs/kbn_serverless_common_settings.mdx b/api_docs/kbn_serverless_common_settings.mdx index 883c43482fabc..358183ba435ec 100644 --- a/api_docs/kbn_serverless_common_settings.mdx +++ b/api_docs/kbn_serverless_common_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-common-settings title: "@kbn/serverless-common-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-common-settings plugin -date: 2024-10-29 +date: 2024-11-05 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 015186f3db80d..75a00c5e9896d 100644 --- a/api_docs/kbn_serverless_observability_settings.mdx +++ b/api_docs/kbn_serverless_observability_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-observability-settings title: "@kbn/serverless-observability-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-observability-settings plugin -date: 2024-10-29 +date: 2024-11-05 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 f1fd121d09fcf..a32fc42e9f895 100644 --- a/api_docs/kbn_serverless_project_switcher.mdx +++ b/api_docs/kbn_serverless_project_switcher.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-project-switcher title: "@kbn/serverless-project-switcher" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-project-switcher plugin -date: 2024-10-29 +date: 2024-11-05 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 fa080c27c7e43..e94566b4cdd7d 100644 --- a/api_docs/kbn_serverless_search_settings.mdx +++ b/api_docs/kbn_serverless_search_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-search-settings title: "@kbn/serverless-search-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-search-settings plugin -date: 2024-10-29 +date: 2024-11-05 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 2c622a25b6c2e..a7849341f38ff 100644 --- a/api_docs/kbn_serverless_security_settings.mdx +++ b/api_docs/kbn_serverless_security_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-security-settings title: "@kbn/serverless-security-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-security-settings plugin -date: 2024-10-29 +date: 2024-11-05 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 7c81c6b809ce9..1062d5fd25bd9 100644 --- a/api_docs/kbn_serverless_storybook_config.mdx +++ b/api_docs/kbn_serverless_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-storybook-config title: "@kbn/serverless-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-storybook-config plugin -date: 2024-10-29 +date: 2024-11-05 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 f5bf024eabc24..4b7f8be8f0688 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2024-10-29 +date: 2024-11-05 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 188a24c84f6ec..9f8779c0e4fdb 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2024-10-29 +date: 2024-11-05 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 e789d66d4bf24..71d2ae705cef2 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2024-10-29 +date: 2024-11-05 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 df02ab9f915e9..24bcaca0c3ca3 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-toolbar'] --- import kbnSharedUxButtonToolbarObj from './kbn_shared_ux_button_toolbar.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index a53686d74d989..e74c73a03fa1b 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2024-10-29 +date: 2024-11-05 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 4cb55ec52ebef..e7e7b86526918 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] --- import kbnSharedUxCardNoDataMocksObj from './kbn_shared_ux_card_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_chrome_navigation.mdx b/api_docs/kbn_shared_ux_chrome_navigation.mdx index 0816afb400258..ce6fce991c5d8 100644 --- a/api_docs/kbn_shared_ux_chrome_navigation.mdx +++ b/api_docs/kbn_shared_ux_chrome_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-chrome-navigation title: "@kbn/shared-ux-chrome-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-chrome-navigation plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-chrome-navigation'] --- import kbnSharedUxChromeNavigationObj from './kbn_shared_ux_chrome_navigation.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_error_boundary.mdx b/api_docs/kbn_shared_ux_error_boundary.mdx index 5a3e15902d5dc..95872342b5ad2 100644 --- a/api_docs/kbn_shared_ux_error_boundary.mdx +++ b/api_docs/kbn_shared_ux_error_boundary.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-error-boundary title: "@kbn/shared-ux-error-boundary" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-error-boundary plugin -date: 2024-10-29 +date: 2024-11-05 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 366c523e8d3f7..9d4949d96c00d 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2024-10-29 +date: 2024-11-05 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 358dc6b152286..95b1156cf421a 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2024-10-29 +date: 2024-11-05 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 7934c4b2d202e..c9e35802cf654 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 01887d49b64ec..5352630fd8299 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 b584c639d4c1a..63e5de70fc973 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2024-10-29 +date: 2024-11-05 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 7c9454dba6091..2a336cf2527e6 100644 --- a/api_docs/kbn_shared_ux_file_types.mdx +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-types title: "@kbn/shared-ux-file-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-types plugin -date: 2024-10-29 +date: 2024-11-05 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 7ba1d25553035..ba7f85b9ebc4f 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2024-10-29 +date: 2024-11-05 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 4174657a7fb5f..d7399a98aca64 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-util'] --- import kbnSharedUxFileUtilObj from './kbn_shared_ux_file_util.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app.mdx b/api_docs/kbn_shared_ux_link_redirect_app.mdx index 15d2c099b15f5..6951563939791 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2024-10-29 +date: 2024-11-05 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 984517f26d2fa..d9867d9a66cc4 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 d77ab3f81f38a..64a8dca9f81cf 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2024-10-29 +date: 2024-11-05 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 3039730643c8e..8ec5f8007cf4b 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 e7d4c421e3717..ded9647408e40 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2024-10-29 +date: 2024-11-05 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 7f4250882668d..2175ce0768063 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 d7dd5d2ba4407..da39ef0d6f846 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2024-10-29 +date: 2024-11-05 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 027cc691f2609..651b52969f275 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 7a1c869fc1e2d..b8b9ace1a6583 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2024-10-29 +date: 2024-11-05 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 6ddab7c636663..89b324e021c3e 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 ba069b143152b..0b619fe541361 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2024-10-29 +date: 2024-11-05 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 3d3deb93bc283..f02170c53d8e2 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2024-10-29 +date: 2024-11-05 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 6d8fbc998404c..f955ecd76ed7d 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 3cec33f873835..36d18436a2102 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 f1712e2145dec..e92b9430928f8 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2024-10-29 +date: 2024-11-05 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 bea6a52e49997..6727255f7be84 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2024-10-29 +date: 2024-11-05 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 9a1bdaad4c166..1fe597b53b5fa 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 d4ae57c34e6ed..cfad6ea87828b 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2024-10-29 +date: 2024-11-05 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 3b4cc1e98bc97..1f2ebe55e6937 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2024-10-29 +date: 2024-11-05 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 8e45c9eaa9c7f..1d43c6b8ead19 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2024-10-29 +date: 2024-11-05 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 8af223ca2c8e9..707b21f1a6ac0 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2024-10-29 +date: 2024-11-05 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 95c1456629de5..1f302dbdcb3a9 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] --- import kbnSharedUxStorybookMockObj from './kbn_shared_ux_storybook_mock.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_tabbed_modal.mdx b/api_docs/kbn_shared_ux_tabbed_modal.mdx index e6c1736c5bca8..6e5837c70564c 100644 --- a/api_docs/kbn_shared_ux_tabbed_modal.mdx +++ b/api_docs/kbn_shared_ux_tabbed_modal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-tabbed-modal title: "@kbn/shared-ux-tabbed-modal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-tabbed-modal plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-tabbed-modal'] --- import kbnSharedUxTabbedModalObj from './kbn_shared_ux_tabbed_modal.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_table_persist.mdx b/api_docs/kbn_shared_ux_table_persist.mdx index bc62c615819bc..e50d7bea6ee29 100644 --- a/api_docs/kbn_shared_ux_table_persist.mdx +++ b/api_docs/kbn_shared_ux_table_persist.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-table-persist title: "@kbn/shared-ux-table-persist" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-table-persist plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-table-persist'] --- import kbnSharedUxTablePersistObj from './kbn_shared_ux_table_persist.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index a5f5cd2083292..d52ac719cc557 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_slo_schema.devdocs.json b/api_docs/kbn_slo_schema.devdocs.json index d83e3fa5fc3d5..13f17bcf471b8 100644 --- a/api_docs/kbn_slo_schema.devdocs.json +++ b/api_docs/kbn_slo_schema.devdocs.json @@ -3225,8 +3225,8 @@ ", string, unknown>; }>]>; }>, ", "PartialC", "<{ id: ", - "StringC", - "; settings: ", + "Type", + "; settings: ", "PartialC", "<{ syncDelay: ", "Type", @@ -3287,8 +3287,8 @@ "signature": [ "TypeC", "<{ id: ", - "StringC", - "; }>" + "Type", + "; }>" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/routes/create.ts", "deprecated": false, @@ -3349,8 +3349,8 @@ "<[", "TypeC", "<{ sloId: ", - "StringC", - "; instanceId: ", + "Type", + "; instanceId: ", "StringC", "; }>, ", "PartialC", @@ -3375,8 +3375,8 @@ "<{ path: ", "TypeC", "<{ id: ", - "StringC", - "; }>; }>" + "Type", + "; }>; }>" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/routes/delete.ts", "deprecated": false, @@ -3449,8 +3449,8 @@ "<[", "TypeC", "<{ sloId: ", - "StringC", - "; instanceId: ", + "Type", + "; instanceId: ", "StringC", "; timeWindow: ", "UnionC", @@ -3553,8 +3553,8 @@ "<", "TypeC", "<{ sloId: ", - "StringC", - "; instanceId: ", + "Type", + "; instanceId: ", "UnionC", "<[", "LiteralC", @@ -3611,8 +3611,8 @@ "<", "TypeC", "<{ sloId: ", - "StringC", - "; sloInstanceId: ", + "Type", + "; sloInstanceId: ", "UnionC", "<[", "LiteralC", @@ -3637,8 +3637,8 @@ "<", "TypeC", "<{ sloId: ", - "StringC", - "; sloInstanceId: ", + "Type", + "; sloInstanceId: ", "UnionC", "<[", "LiteralC", @@ -3783,8 +3783,8 @@ "<", "TypeC", "<{ id: ", - "StringC", - "; name: ", + "Type", + "; name: ", "StringC", "; description: ", "StringC", @@ -5423,8 +5423,8 @@ "<[", "TypeC", "<{ id: ", - "StringC", - "; name: ", + "Type", + "; name: ", "StringC", "; description: ", "StringC", @@ -8525,8 +8525,8 @@ "<{ path: ", "TypeC", "<{ id: ", - "StringC", - "; }>; }>, ", + "Type", + "; }>; }>, ", "PartialC", "<{ query: ", "PartialC", @@ -8557,8 +8557,8 @@ "<[", "TypeC", "<{ id: ", - "StringC", - "; name: ", + "Type", + "; name: ", "StringC", "; description: ", "StringC", @@ -12262,8 +12262,8 @@ "<{ path: ", "TypeC", "<{ id: ", - "StringC", - "; }>; }>" + "Type", + "; }>; }>" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/routes/manage.ts", "deprecated": false, @@ -13038,8 +13038,8 @@ "<{ path: ", "TypeC", "<{ id: ", - "StringC", - "; }>; }>" + "Type", + "; }>; }>" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/routes/reset.ts", "deprecated": false, @@ -13056,8 +13056,8 @@ "signature": [ "TypeC", "<{ id: ", - "StringC", - "; name: ", + "Type", + "; name: ", "StringC", "; description: ", "StringC", @@ -14594,8 +14594,8 @@ "signature": [ "TypeC", "<{ id: ", - "StringC", - "; name: ", + "Type", + "; name: ", "StringC", "; description: ", "StringC", @@ -16112,7 +16112,8 @@ "label": "sloIdSchema", "description": [], "signature": [ - "StringC" + "Type", + "" ], "path": "x-pack/packages/kbn-slo-schema/src/schema/slo.ts", "deprecated": false, @@ -16173,8 +16174,8 @@ "<[", "TypeC", "<{ id: ", - "StringC", - "; name: ", + "Type", + "; name: ", "StringC", "; description: ", "StringC", @@ -18827,8 +18828,8 @@ "<{ path: ", "TypeC", "<{ id: ", - "StringC", - "; }>; body: ", + "Type", + "; }>; body: ", "PartialC", "<{ name: ", "StringC", @@ -20275,8 +20276,8 @@ "signature": [ "TypeC", "<{ id: ", - "StringC", - "; name: ", + "Type", + "; name: ", "StringC", "; description: ", "StringC", diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index da73f4818c9b3..fb25940e45c6b 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2024-10-29 +date: 2024-11-05 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 3f6091d85d25d..4197b39d9380a 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_sort_predicates.mdx b/api_docs/kbn_sort_predicates.mdx index 64f95bc4686ee..32ae580e6fde5 100644 --- a/api_docs/kbn_sort_predicates.mdx +++ b/api_docs/kbn_sort_predicates.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sort-predicates title: "@kbn/sort-predicates" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sort-predicates plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-predicates'] --- import kbnSortPredicatesObj from './kbn_sort_predicates.devdocs.json'; diff --git a/api_docs/kbn_sse_utils.mdx b/api_docs/kbn_sse_utils.mdx index 24629ee7e105f..456b22129bc6a 100644 --- a/api_docs/kbn_sse_utils.mdx +++ b/api_docs/kbn_sse_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sse-utils title: "@kbn/sse-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sse-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sse-utils'] --- import kbnSseUtilsObj from './kbn_sse_utils.devdocs.json'; diff --git a/api_docs/kbn_sse_utils_client.mdx b/api_docs/kbn_sse_utils_client.mdx index edb75018e6d9a..b0434d0037c9d 100644 --- a/api_docs/kbn_sse_utils_client.mdx +++ b/api_docs/kbn_sse_utils_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sse-utils-client title: "@kbn/sse-utils-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sse-utils-client plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sse-utils-client'] --- import kbnSseUtilsClientObj from './kbn_sse_utils_client.devdocs.json'; diff --git a/api_docs/kbn_sse_utils_server.mdx b/api_docs/kbn_sse_utils_server.mdx index dd128b9304c98..a274948632262 100644 --- a/api_docs/kbn_sse_utils_server.mdx +++ b/api_docs/kbn_sse_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sse-utils-server title: "@kbn/sse-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sse-utils-server plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sse-utils-server'] --- import kbnSseUtilsServerObj from './kbn_sse_utils_server.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 6db1e4a125398..5938ac7bbf319 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2024-10-29 +date: 2024-11-05 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 07c58b5a73168..3246bcffd9d6a 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2024-10-29 +date: 2024-11-05 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 6e836b362c4d9..bab5b5c0846e8 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_synthetics_e2e.mdx b/api_docs/kbn_synthetics_e2e.mdx index 3b045981a3e89..441f909b26c36 100644 --- a/api_docs/kbn_synthetics_e2e.mdx +++ b/api_docs/kbn_synthetics_e2e.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-synthetics-e2e title: "@kbn/synthetics-e2e" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/synthetics-e2e plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/synthetics-e2e'] --- import kbnSyntheticsE2eObj from './kbn_synthetics_e2e.devdocs.json'; diff --git a/api_docs/kbn_synthetics_private_location.mdx b/api_docs/kbn_synthetics_private_location.mdx index 75caefb12969b..2010f4aef3d7a 100644 --- a/api_docs/kbn_synthetics_private_location.mdx +++ b/api_docs/kbn_synthetics_private_location.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-synthetics-private-location title: "@kbn/synthetics-private-location" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/synthetics-private-location plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/synthetics-private-location'] --- import kbnSyntheticsPrivateLocationObj from './kbn_synthetics_private_location.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index 2187a9c8a47dc..c843a43ec2451 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] --- import kbnTelemetryToolsObj from './kbn_telemetry_tools.devdocs.json'; diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index d7d86a4df5901..8ccf9a68aa3a0 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_eui_helpers.mdx b/api_docs/kbn_test_eui_helpers.mdx index 21f26e2cd72bc..4c1683b00e31a 100644 --- a/api_docs/kbn_test_eui_helpers.mdx +++ b/api_docs/kbn_test_eui_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-eui-helpers title: "@kbn/test-eui-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-eui-helpers plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-eui-helpers'] --- import kbnTestEuiHelpersObj from './kbn_test_eui_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index f6fd9c9e003bb..ed6cd1b5e0881 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2024-10-29 +date: 2024-11-05 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 4dc65163535b1..69387623d8462 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_timerange.mdx b/api_docs/kbn_timerange.mdx index a635fad0174b5..46bc4e0410d57 100644 --- a/api_docs/kbn_timerange.mdx +++ b/api_docs/kbn_timerange.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-timerange title: "@kbn/timerange" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/timerange plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/timerange'] --- import kbnTimerangeObj from './kbn_timerange.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index eceadabba223a..f2e3d765e05fe 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_transpose_utils.mdx b/api_docs/kbn_transpose_utils.mdx index f651f54c856c8..222b8ca073153 100644 --- a/api_docs/kbn_transpose_utils.mdx +++ b/api_docs/kbn_transpose_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-transpose-utils title: "@kbn/transpose-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/transpose-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/transpose-utils'] --- import kbnTransposeUtilsObj from './kbn_transpose_utils.devdocs.json'; diff --git a/api_docs/kbn_triggers_actions_ui_types.mdx b/api_docs/kbn_triggers_actions_ui_types.mdx index 2736fb8d9baef..016a6a831a12f 100644 --- a/api_docs/kbn_triggers_actions_ui_types.mdx +++ b/api_docs/kbn_triggers_actions_ui_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-triggers-actions-ui-types title: "@kbn/triggers-actions-ui-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/triggers-actions-ui-types plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/triggers-actions-ui-types'] --- import kbnTriggersActionsUiTypesObj from './kbn_triggers_actions_ui_types.devdocs.json'; diff --git a/api_docs/kbn_try_in_console.mdx b/api_docs/kbn_try_in_console.mdx index 0a37a086b7fdf..c988e21414d54 100644 --- a/api_docs/kbn_try_in_console.mdx +++ b/api_docs/kbn_try_in_console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-try-in-console title: "@kbn/try-in-console" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/try-in-console plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/try-in-console'] --- import kbnTryInConsoleObj from './kbn_try_in_console.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index e7b8645e03e80..cf409e6949d80 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2024-10-29 +date: 2024-11-05 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 413418e47f95b..067725bc6e306 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/typed-react-router-config'] --- import kbnTypedReactRouterConfigObj from './kbn_typed_react_router_config.devdocs.json'; diff --git a/api_docs/kbn_ui_actions_browser.mdx b/api_docs/kbn_ui_actions_browser.mdx index 195c18f0ff2e6..ac1fd498da73a 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-actions-browser'] --- import kbnUiActionsBrowserObj from './kbn_ui_actions_browser.devdocs.json'; diff --git a/api_docs/kbn_ui_shared_deps_src.mdx b/api_docs/kbn_ui_shared_deps_src.mdx index 3963aa1bc0167..79a77c8483b52 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2024-10-29 +date: 2024-11-05 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 68308496b1762..beed91f429ae2 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2024-10-29 +date: 2024-11-05 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 053a8c6ed9e0c..57a9c679ecf5c 100644 --- a/api_docs/kbn_unified_data_table.mdx +++ b/api_docs/kbn_unified_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-data-table title: "@kbn/unified-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-data-table plugin -date: 2024-10-29 +date: 2024-11-05 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 f4aff957a78d5..10a5c839107bb 100644 --- a/api_docs/kbn_unified_doc_viewer.mdx +++ b/api_docs/kbn_unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-doc-viewer title: "@kbn/unified-doc-viewer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-doc-viewer plugin -date: 2024-10-29 +date: 2024-11-05 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 4c30382cef8ce..e87ee28f28d68 100644 --- a/api_docs/kbn_unified_field_list.mdx +++ b/api_docs/kbn_unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-field-list title: "@kbn/unified-field-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-field-list plugin -date: 2024-10-29 +date: 2024-11-05 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 4fa8519ac696a..2bb62cff7819f 100644 --- a/api_docs/kbn_unsaved_changes_badge.mdx +++ b/api_docs/kbn_unsaved_changes_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unsaved-changes-badge title: "@kbn/unsaved-changes-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unsaved-changes-badge plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unsaved-changes-badge'] --- import kbnUnsavedChangesBadgeObj from './kbn_unsaved_changes_badge.devdocs.json'; diff --git a/api_docs/kbn_unsaved_changes_prompt.mdx b/api_docs/kbn_unsaved_changes_prompt.mdx index 608d1678b7d51..80b3ce7a51317 100644 --- a/api_docs/kbn_unsaved_changes_prompt.mdx +++ b/api_docs/kbn_unsaved_changes_prompt.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unsaved-changes-prompt title: "@kbn/unsaved-changes-prompt" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unsaved-changes-prompt plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unsaved-changes-prompt'] --- import kbnUnsavedChangesPromptObj from './kbn_unsaved_changes_prompt.devdocs.json'; diff --git a/api_docs/kbn_use_tracked_promise.mdx b/api_docs/kbn_use_tracked_promise.mdx index 970ae0b4b5997..5d443797d0ed2 100644 --- a/api_docs/kbn_use_tracked_promise.mdx +++ b/api_docs/kbn_use_tracked_promise.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-use-tracked-promise title: "@kbn/use-tracked-promise" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/use-tracked-promise plugin -date: 2024-10-29 +date: 2024-11-05 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 6cc8511f86f01..1c4f6115d462d 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2024-10-29 +date: 2024-11-05 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 fa8446ade477b..0a1642ac9fbee 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2024-10-29 +date: 2024-11-05 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 3485a0594d65d..2f9a113e5215c 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types-jest'] --- import kbnUtilityTypesJestObj from './kbn_utility_types_jest.devdocs.json'; diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index fc954adb62df8..298b4e1231a29 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_visualization_ui_components.mdx b/api_docs/kbn_visualization_ui_components.mdx index 97486a4e0f2ee..803576e950ac8 100644 --- a/api_docs/kbn_visualization_ui_components.mdx +++ b/api_docs/kbn_visualization_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-ui-components title: "@kbn/visualization-ui-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-ui-components plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-ui-components'] --- import kbnVisualizationUiComponentsObj from './kbn_visualization_ui_components.devdocs.json'; diff --git a/api_docs/kbn_visualization_utils.mdx b/api_docs/kbn_visualization_utils.mdx index 648d7a7975ffc..a0f0888ad0961 100644 --- a/api_docs/kbn_visualization_utils.mdx +++ b/api_docs/kbn_visualization_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-utils title: "@kbn/visualization-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-utils plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-utils'] --- import kbnVisualizationUtilsObj from './kbn_visualization_utils.devdocs.json'; diff --git a/api_docs/kbn_xstate_utils.mdx b/api_docs/kbn_xstate_utils.mdx index 209f4d7966d88..8fd7b4c274197 100644 --- a/api_docs/kbn_xstate_utils.mdx +++ b/api_docs/kbn_xstate_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-xstate-utils title: "@kbn/xstate-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/xstate-utils plugin -date: 2024-10-29 +date: 2024-11-05 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 70f77e4f61ee1..69e51986989ff 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2024-10-29 +date: 2024-11-05 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.mdx b/api_docs/kbn_zod.mdx index bd69f8e1e2ebf..359f0b380d7a6 100644 --- a/api_docs/kbn_zod.mdx +++ b/api_docs/kbn_zod.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-zod title: "@kbn/zod" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/zod plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/zod'] --- import kbnZodObj from './kbn_zod.devdocs.json'; diff --git a/api_docs/kbn_zod_helpers.mdx b/api_docs/kbn_zod_helpers.mdx index 9edb0995d158c..872422ef8eaa2 100644 --- a/api_docs/kbn_zod_helpers.mdx +++ b/api_docs/kbn_zod_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-zod-helpers title: "@kbn/zod-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/zod-helpers plugin -date: 2024-10-29 +date: 2024-11-05 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 7fc7de83e5b4f..17c207239a1ca 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] --- import kibanaOverviewObj from './kibana_overview.devdocs.json'; diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index a9a94d47bb29b..94890b4e1fb3e 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2024-10-29 +date: 2024-11-05 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 5e666d30f4e39..2ae2c43759dd2 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2024-10-29 +date: 2024-11-05 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 92ec567429653..ebe248aa10663 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index af161c830af13..e68711623a2c6 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index de0d742a6747d..61d7cde08013e 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2024-10-29 +date: 2024-11-05 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 62ae45842b17f..9f510577cdf8f 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] --- import licenseManagementObj from './license_management.devdocs.json'; diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index 21572811dbc03..bee86fc368cb4 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2024-10-29 +date: 2024-11-05 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 3a1feebc2b790..52461f3464514 100644 --- a/api_docs/links.mdx +++ b/api_docs/links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/links title: "links" image: https://source.unsplash.com/400x175/?github description: API docs for the links plugin -date: 2024-10-29 +date: 2024-11-05 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 f3c02c51f8c29..6fae39697ac5e 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/logs_data_access.mdx b/api_docs/logs_data_access.mdx index c010844a6a562..2d4ff4b5fc2f0 100644 --- a/api_docs/logs_data_access.mdx +++ b/api_docs/logs_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsDataAccess title: "logsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the logsDataAccess plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsDataAccess'] --- import logsDataAccessObj from './logs_data_access.devdocs.json'; diff --git a/api_docs/logs_explorer.mdx b/api_docs/logs_explorer.mdx index 01773c8ec99c6..f1ca50bdbe7af 100644 --- a/api_docs/logs_explorer.mdx +++ b/api_docs/logs_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsExplorer title: "logsExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the logsExplorer plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsExplorer'] --- import logsExplorerObj from './logs_explorer.devdocs.json'; diff --git a/api_docs/logs_shared.mdx b/api_docs/logs_shared.mdx index 7a8c032479cdd..218c6e4fc9fc9 100644 --- a/api_docs/logs_shared.mdx +++ b/api_docs/logs_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsShared title: "logsShared" image: https://source.unsplash.com/400x175/?github description: API docs for the logsShared plugin -date: 2024-10-29 +date: 2024-11-05 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 fb30ef399c3b1..2f1f15ace3926 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2024-10-29 +date: 2024-11-05 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 abafcb455e509..7b55f1eb5704f 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2024-10-29 +date: 2024-11-05 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 dfdb7e586edf4..c4beb57cc13e8 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/metrics_data_access.mdx b/api_docs/metrics_data_access.mdx index a2bc3db44a3bc..2c31341a91867 100644 --- a/api_docs/metrics_data_access.mdx +++ b/api_docs/metrics_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/metricsDataAccess title: "metricsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the metricsDataAccess plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'metricsDataAccess'] --- import metricsDataAccessObj from './metrics_data_access.devdocs.json'; diff --git a/api_docs/ml.devdocs.json b/api_docs/ml.devdocs.json index 8b9d3866bfad7..e6ce179e5ada1 100644 --- a/api_docs/ml.devdocs.json +++ b/api_docs/ml.devdocs.json @@ -1253,7 +1253,9 @@ "MlDetector", "; }): Promise; forecast({ jobId, duration, neverExpires, }: { jobId: string; duration?: string | undefined; neverExpires?: boolean | undefined; }): Promise; deleteForecast({ jobId, forecastId }: { jobId: string; forecastId: string; }): Promise<", "DeleteForecastResponse", - ">; overallBuckets({ jobId, topN, bucketSpan, start, end, overallScore, }: { jobId: string; topN: string; bucketSpan: string; start: number; end: number; overallScore?: number | undefined; }): Promise; hasPrivileges(obj: any): Promise<", + ">; overallBuckets({ jobId, topN, bucketSpan, start, end, overallScore, }: { jobId: string[]; topN: string; bucketSpan: string; start: number; end: number; overallScore?: number | undefined; }): Promise<", + "MlGetOverallBucketsResponse", + ">; hasPrivileges(obj: any): Promise<", "MlHasPrivilegesResponse", ">; checkMlCapabilities(): Promise<", "MlCapabilitiesResponse", diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index f4401a9d19085..8e48e1f03d4a5 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/mock_idp_plugin.mdx b/api_docs/mock_idp_plugin.mdx index aa9bf48e602fb..9fea23034943e 100644 --- a/api_docs/mock_idp_plugin.mdx +++ b/api_docs/mock_idp_plugin.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mockIdpPlugin title: "mockIdpPlugin" image: https://source.unsplash.com/400x175/?github description: API docs for the mockIdpPlugin plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mockIdpPlugin'] --- import mockIdpPluginObj from './mock_idp_plugin.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index 780e5c774f531..35439ffa5beac 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2024-10-29 +date: 2024-11-05 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 47c4b92dad987..bb6e9f4e2431f 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.devdocs.json b/api_docs/navigation.devdocs.json index d1c3a65dac7cc..9e31f4d2ddd76 100644 --- a/api_docs/navigation.devdocs.json +++ b/api_docs/navigation.devdocs.json @@ -379,7 +379,7 @@ "id": "def-public.TopNavMenuItems.$1", "type": "Object", "tags": [], - "label": "{\n config,\n className,\n popoverBreakpoints,\n}", + "label": "{\n config,\n className,\n popoverBreakpoints = POPOVER_BREAKPOINTS,\n}", "description": [], "signature": [ "TopNavMenuItemsProps" @@ -780,6 +780,20 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "navigation", + "id": "def-public.TopNavMenuData.iconOnly", + "type": "CompoundType", + "tags": [], + "label": "iconOnly", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/navigation/public/top_nav_menu/top_nav_menu_data.tsx", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "navigation", "id": "def-public.TopNavMenuData.target", diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index 3ac147e7dc750..86c543ac030de 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.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 | |-------------------|-----------|------------------------|-----------------| -| 55 | 0 | 53 | 4 | +| 56 | 0 | 54 | 4 | ## Client diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index c701b19f9d838..82d3d2854b91e 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2024-10-29 +date: 2024-11-05 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 66faaeda7fac7..448bc7a94b9a2 100644 --- a/api_docs/no_data_page.mdx +++ b/api_docs/no_data_page.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/noDataPage title: "noDataPage" image: https://source.unsplash.com/400x175/?github description: API docs for the noDataPage plugin -date: 2024-10-29 +date: 2024-11-05 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 1ab532f6e8654..7415cf7989abd 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'notifications'] --- import notificationsObj from './notifications.devdocs.json'; diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 048ddb25efaa5..b1ecd7ad0e778 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant.devdocs.json b/api_docs/observability_a_i_assistant.devdocs.json index eed6ed85438f3..fb3350bd038fd 100644 --- a/api_docs/observability_a_i_assistant.devdocs.json +++ b/api_docs/observability_a_i_assistant.devdocs.json @@ -6826,7 +6826,15 @@ "section": "def-common.Message", "text": "Message" }, - "; }" + " & { scopes: ", + { + "pluginId": "@kbn/ai-assistant-common", + "scope": "common", + "docId": "kibKbnAiAssistantCommonPluginApi", + "section": "def-common.AssistantScope", + "text": "AssistantScope" + }, + "[]; }; }" ], "path": "x-pack/plugins/observability_solution/observability_ai_assistant/public/analytics/index.ts", "deprecated": false, diff --git a/api_docs/observability_a_i_assistant.mdx b/api_docs/observability_a_i_assistant.mdx index 48f8c9ad8e516..07fe0a767f392 100644 --- a/api_docs/observability_a_i_assistant.mdx +++ b/api_docs/observability_a_i_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistant title: "observabilityAIAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistant plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistant'] --- import observabilityAIAssistantObj from './observability_a_i_assistant.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant_app.mdx b/api_docs/observability_a_i_assistant_app.mdx index 1d46f31b696ca..7aa15c94bd864 100644 --- a/api_docs/observability_a_i_assistant_app.mdx +++ b/api_docs/observability_a_i_assistant_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistantApp title: "observabilityAIAssistantApp" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistantApp plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistantApp'] --- import observabilityAIAssistantAppObj from './observability_a_i_assistant_app.devdocs.json'; diff --git a/api_docs/observability_ai_assistant_management.mdx b/api_docs/observability_ai_assistant_management.mdx index 3bde743ca1288..2a5457a29ea6f 100644 --- a/api_docs/observability_ai_assistant_management.mdx +++ b/api_docs/observability_ai_assistant_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAiAssistantManagement title: "observabilityAiAssistantManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAiAssistantManagement plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAiAssistantManagement'] --- import observabilityAiAssistantManagementObj from './observability_ai_assistant_management.devdocs.json'; diff --git a/api_docs/observability_logs_explorer.mdx b/api_docs/observability_logs_explorer.mdx index 32cc2ced144a6..c3429f9415b35 100644 --- a/api_docs/observability_logs_explorer.mdx +++ b/api_docs/observability_logs_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityLogsExplorer title: "observabilityLogsExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityLogsExplorer plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityLogsExplorer'] --- import observabilityLogsExplorerObj from './observability_logs_explorer.devdocs.json'; diff --git a/api_docs/observability_onboarding.mdx b/api_docs/observability_onboarding.mdx index be5762cab1112..379013a23b83c 100644 --- a/api_docs/observability_onboarding.mdx +++ b/api_docs/observability_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityOnboarding title: "observabilityOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityOnboarding plugin -date: 2024-10-29 +date: 2024-11-05 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 cbcd73348b303..337e3fbdf2769 100644 --- a/api_docs/observability_shared.devdocs.json +++ b/api_docs/observability_shared.devdocs.json @@ -1541,7 +1541,9 @@ "type": "Function", "tags": [], "label": "useBreadcrumbs", - "description": [], + "description": [ + "\n\nBy default the breadcrumbs will be passed to either serverless.setBreadcrumbs or chrome.setBreadcrumbs depending on the\nenvironment. The breadcrumbs will *also* be passed to the project style breadcrumbs for stateful project style. We will use \"project style\"\nhere to refer to serverless chrome and stateful project style chrome. Classic refers to stateful classic chrome.\n\nProject style breadcrumbs add a root crumb (\"deployment\" etc) and \"nav crumbs\" which are derived from the navigation structure. By default\nthe \"absolute\" mode is used which means the breadcrumbs passed here will omit the navigation derived \"nav crumbs\". You can pass\nabsoluteProjectStyleBreadcrumbs: false to include the 'smart' \"nav crumbs\".\n\nIn classic mode (not project style) the 'Observability' crumb is added.\n\nYou can also pass classicOnly to only set breadrumbs in the classic chrome context. This can be useful if your solution just wants to defer all project style crumbs to the \"nav crumbs\"." + ], "signature": [ "(extraCrumbs: ", { @@ -1567,7 +1569,7 @@ "section": "def-public.ServerlessPluginStart", "text": "ServerlessPluginStart" }, - " | undefined; } | undefined) => void" + " | undefined; absoluteProjectStyleBreadcrumbs?: boolean | undefined; classicOnly?: boolean | undefined; } | undefined) => void" ], "path": "x-pack/plugins/observability_solution/observability_shared/public/hooks/use_breadcrumbs.ts", "deprecated": false, @@ -1661,6 +1663,34 @@ "path": "x-pack/plugins/observability_solution/observability_shared/public/hooks/use_breadcrumbs.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.useBreadcrumbs.$2.absoluteProjectStyleBreadcrumbs", + "type": "CompoundType", + "tags": [], + "label": "absoluteProjectStyleBreadcrumbs", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/observability_solution/observability_shared/public/hooks/use_breadcrumbs.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.useBreadcrumbs.$2.classicOnly", + "type": "CompoundType", + "tags": [], + "label": "classicOnly", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/observability_solution/observability_shared/public/hooks/use_breadcrumbs.ts", + "deprecated": false, + "trackAdoption": false } ] } @@ -6448,18 +6478,6 @@ "trackAdoption": false, "initialIsOpen": false }, - { - "parentPluginId": "observabilityShared", - "id": "def-common.EntityType", - "type": "Enum", - "tags": [], - "label": "EntityType", - "description": [], - "path": "x-pack/plugins/observability_solution/observability_shared/common/entity/entity_types.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, { "parentPluginId": "observabilityShared", "id": "def-common.IndexLifecyclePhaseSelectOption", @@ -8208,6 +8226,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "observabilityShared", + "id": "def-common.SERVICE_OVERVIEW_LOCATOR_ID", + "type": "string", + "tags": [], + "label": "SERVICE_OVERVIEW_LOCATOR_ID", + "description": [], + "signature": [ + "\"serviceOverviewLocator\"" + ], + "path": "x-pack/plugins/observability_solution/observability_shared/common/locators/apm/service_overview_locator.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "observabilityShared", "id": "def-common.SERVICE_RUNTIME_NAME", @@ -9107,6 +9140,21 @@ } ], "objects": [ + { + "parentPluginId": "observabilityShared", + "id": "def-common.ENTITY_TYPES", + "type": "Object", + "tags": [], + "label": "ENTITY_TYPES", + "description": [], + "signature": [ + "{ readonly HOST: \"host\"; readonly CONTAINER: \"container\"; readonly SERVICE: \"service\"; readonly KUBERNETES: { readonly CLUSTER: { ecs: \"kubernetes_cluster_ecs\"; semconv: \"kubernetes_cluster_semconv\"; }; readonly CONTAINER: { ecs: \"kubernetes_container_ecs\"; semconv: \"kubernetes_container_semconv\"; }; readonly CRONJOB: { ecs: \"kubernetes_cron_job_ecs\"; semconv: \"kubernetes_cron_job_semconv\"; }; readonly DAEMONSET: { ecs: \"kubernetes_daemon_set_ecs\"; semconv: \"kubernetes_daemon_set_semconv\"; }; readonly DEPLOYMENT: { ecs: \"kubernetes_deployment_ecs\"; semconv: \"kubernetes_deployment_semconv\"; }; readonly JOB: { ecs: \"kubernetes_job_ecs\"; semconv: \"kubernetes_job_semconv\"; }; readonly NAMESPACE: { ecs: \"kubernetes_namespace_ecs\"; semconv: \"kubernetes_namespace_semconv\"; }; readonly NODE: { ecs: \"kubernetes_node_ecs\"; semconv: \"kubernetes_node_semconv\"; }; readonly POD: { ecs: \"kubernetes_pod_ecs\"; semconv: \"kubernetes_pod_semconv\"; }; readonly STATEFULSET: { ecs: \"kubernetes_stateful_set_ecs\"; semconv: \"kubernetes_stateful_set_semconv\"; }; }; }" + ], + "path": "x-pack/plugins/observability_solution/observability_shared/common/entity/entity_types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "observabilityShared", "id": "def-common.indexLifeCyclePhaseToDataTier", diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index da2e22d6107d7..668951f316d89 100644 --- a/api_docs/observability_shared.mdx +++ b/api_docs/observability_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityShared title: "observabilityShared" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityShared plugin -date: 2024-10-29 +date: 2024-11-05 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 | |-------------------|-----------|------------------------|-----------------| -| 505 | 1 | 500 | 19 | +| 508 | 1 | 502 | 19 | ## Client diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 4473b2dab7d9d..1ec492abc682b 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2024-10-29 +date: 2024-11-05 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 7295ee3d3fb6e..36065ec377729 100644 --- a/api_docs/painless_lab.mdx +++ b/api_docs/painless_lab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/painlessLab title: "painlessLab" image: https://source.unsplash.com/400x175/?github description: API docs for the painlessLab plugin -date: 2024-10-29 +date: 2024-11-05 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 7f90789c54ae4..3312278c35617 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -21,7 +21,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 54073 | 242 | 40646 | 2019 | +| 54121 | 242 | 40639 | 2002 | ## Plugin Directory @@ -39,7 +39,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@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 | 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) | - | 84 | 0 | 21 | 1 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 83 | 0 | 20 | 1 | | 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 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | Static migration page where self-managed users can see text/copy about migrating to Elastic Cloud | 8 | 0 | 8 | 1 | | | [@elastic/kibana-cloud-security-posture](https://github.com/orgs/elastic/teams/kibana-cloud-security-posture) | Defend for containers (D4C) | 52 | 0 | 43 | 2 | @@ -65,7 +65,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@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 | 4 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | This plugin introduces the concept of data set quality, where users can easily get an overview on the data sets they have. | 14 | 0 | 14 | 8 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 15 | 0 | 9 | 2 | -| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains the Discover application and the saved search embeddable. | 150 | 0 | 102 | 26 | +| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains the Discover application and the saved search embeddable. | 148 | 0 | 100 | 24 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 35 | 0 | 33 | 2 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | A stateful layer to register shared features and provide an access point to discover without a direct dependency | 16 | 0 | 15 | 2 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | APIs used to assess the quality of data in Elasticsearch indexes | 2 | 0 | 0 | 0 | @@ -103,7 +103,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | 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. | 89 | 0 | 89 | 8 | | | [@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 | 3 | 0 | 3 | 0 | -| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1426 | 5 | 1301 | 80 | +| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1423 | 5 | 1300 | 80 | | 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) | - | 72 | 0 | 14 | 5 | | globalSearchBar | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 0 | 0 | 0 | 0 | @@ -114,15 +114,15 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 149 | 0 | 111 | 1 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Image embeddable | 1 | 0 | 1 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 4 | 0 | 4 | 0 | -| | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 250 | 0 | 245 | 1 | -| | [@elastic/appex-ai-infra](https://github.com/orgs/elastic/teams/appex-ai-infra) | - | 49 | 0 | 44 | 15 | +| | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 241 | 0 | 236 | 1 | +| | [@elastic/appex-ai-infra](https://github.com/orgs/elastic/teams/appex-ai-infra) | - | 33 | 0 | 28 | 4 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | This plugin visualizes data from Filebeat and Metricbeat, and integrates with other Observability solutions | 24 | 0 | 24 | 5 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 4 | 0 | 4 | 0 | | inputControlVis | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds Input Control visualization to Kibana | 0 | 0 | 0 | 0 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 127 | 2 | 100 | 4 | | | [@elastic/security-scalability](https://github.com/orgs/elastic/teams/security-scalability) | Plugin implementing the Integration Assistant API and UI | 71 | 0 | 56 | 4 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides UI and APIs for the interactive setup mode. | 28 | 0 | 18 | 0 | -| | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 5 | 0 | 5 | 3 | +| | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 5 | 0 | 5 | 4 | | | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 43 | 0 | 43 | 4 | | | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 5 | 0 | 5 | 2 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 6 | 0 | 6 | 0 | @@ -148,7 +148,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 2 | 0 | 2 | 0 | | | [@elastic/stack-monitoring](https://github.com/orgs/elastic/teams/stack-monitoring) | - | 15 | 3 | 13 | 1 | | | [@elastic/stack-monitoring](https://github.com/orgs/elastic/teams/stack-monitoring) | - | 9 | 0 | 9 | 0 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 55 | 0 | 53 | 4 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 56 | 0 | 54 | 4 | | | [@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 | @@ -158,7 +158,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/obs-ai-assistant](https://github.com/orgs/elastic/teams/obs-ai-assistant) | - | 2 | 0 | 2 | 0 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | This plugin exposes and registers observability log consumption features. | 19 | 0 | 19 | 1 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 24 | 0 | 24 | 0 | -| | [@elastic/observability-ui](https://github.com/orgs/elastic/teams/observability-ui) | - | 505 | 1 | 500 | 19 | +| | [@elastic/observability-ui](https://github.com/orgs/elastic/teams/observability-ui) | - | 508 | 1 | 502 | 19 | | | [@elastic/security-defend-workflows](https://github.com/orgs/elastic/teams/security-defend-workflows) | - | 23 | 0 | 23 | 7 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 2 | 0 | 2 | 0 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds a standardized Presentation panel which allows any forward ref component to interface with various Kibana systems. | 11 | 0 | 11 | 4 | @@ -168,7 +168,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-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. | 9 | 0 | 2 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 21 | 0 | 21 | 0 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 285 | 0 | 248 | 11 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 263 | 0 | 226 | 10 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 24 | 0 | 19 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 114 | 2 | 109 | 5 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 25 | 0 | 25 | 0 | @@ -243,7 +243,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Package name           | Maintaining team | Description | API Cnt | Any Cnt | Missing
comments | Missing
exports | |--------------|----------------|-----------|--------------|----------|---------------|--------| | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 14 | 0 | 14 | 0 | -| | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 63 | 0 | 63 | 1 | +| | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 64 | 0 | 64 | 1 | | | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 3 | 0 | 3 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 36 | 0 | 0 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 2 | 0 | 0 | 0 | @@ -319,7 +319,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) | - | 210 | 0 | 103 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 211 | 0 | 104 | 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) | - | 6 | 0 | 1 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 8 | 0 | 8 | 0 | @@ -492,7 +492,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 4 | 0 | 4 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 3 | 0 | 3 | 0 | | | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 65 | 0 | 53 | 0 | -| | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 17 | 0 | 17 | 0 | +| | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 21 | 0 | 21 | 0 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 5 | 0 | 5 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 8 | 0 | 8 | 0 | @@ -504,7 +504,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 15 | 0 | 9 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 38 | 2 | 33 | 0 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 37 | 0 | 34 | 2 | -| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 234 | 0 | 200 | 4 | +| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 284 | 0 | 234 | 4 | | | [@elastic/docs](https://github.com/orgs/elastic/teams/docs) | - | 79 | 0 | 79 | 2 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 5 | 0 | 5 | 1 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 57 | 0 | 30 | 6 | @@ -518,9 +518,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 32 | 0 | 19 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 11 | 0 | 6 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 271 | 1 | 210 | 14 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 29 | 0 | 29 | 1 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 30 | 0 | 30 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 1 | 0 | -| | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 269 | 1 | 211 | 34 | +| | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 270 | 1 | 211 | 35 | | | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 29 | 0 | 12 | 0 | | | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 79 | 0 | 71 | 0 | | | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 202 | 0 | 190 | 12 | @@ -548,6 +548,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 36 | 0 | 7 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 47 | 0 | 40 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 124 | 3 | 124 | 0 | +| | [@elastic/appex-ai-infra](https://github.com/orgs/elastic/teams/appex-ai-infra) | - | 99 | 0 | 39 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 7 | 1 | 7 | 1 | | | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 9 | 0 | 9 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 52 | 12 | 43 | 0 | @@ -574,7 +575,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 23 | 0 | 7 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 8 | 0 | 2 | 3 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 45 | 0 | 0 | 0 | -| | [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 141 | 0 | 140 | 0 | +| | [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 140 | 0 | 139 | 0 | | | [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 20 | 0 | 11 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 88 | 0 | 10 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 56 | 0 | 6 | 0 | @@ -689,7 +690,6 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 67 | 0 | 40 | 0 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 281 | 1 | 160 | 0 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 74 | 0 | 73 | 0 | -| | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 59 | 0 | 38 | 5 | | | [@elastic/kibana-cloud-security-posture](https://github.com/orgs/elastic/teams/kibana-cloud-security-posture) | - | 7 | 0 | 0 | 0 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 15 | 0 | 15 | 7 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 54 | 0 | 49 | 0 | diff --git a/api_docs/presentation_panel.mdx b/api_docs/presentation_panel.mdx index 0e656daefd615..6fb52d6de7c47 100644 --- a/api_docs/presentation_panel.mdx +++ b/api_docs/presentation_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationPanel title: "presentationPanel" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationPanel plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationPanel'] --- import presentationPanelObj from './presentation_panel.devdocs.json'; diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index afd2c788123f7..8ab442ace4bda 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2024-10-29 +date: 2024-11-05 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 a754c27b9a3af..20e9d08496419 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2024-10-29 +date: 2024-11-05 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 336420a32f05c..8c12cf417e674 100644 --- a/api_docs/profiling_data_access.mdx +++ b/api_docs/profiling_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profilingDataAccess title: "profilingDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the profilingDataAccess plugin -date: 2024-10-29 +date: 2024-11-05 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 b6dbfddf6aa61..73eeec7c0d63e 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2024-10-29 +date: 2024-11-05 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 8e584ade12ea6..1b73501436a29 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2024-10-29 +date: 2024-11-05 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 c29f86f4fd24a..a98ac15e0a04c 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2024-10-29 +date: 2024-11-05 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 d12d867f04393..17ebe064cea2a 100644 --- a/api_docs/rule_registry.devdocs.json +++ b/api_docs/rule_registry.devdocs.json @@ -1879,266 +1879,6 @@ "returnComment": [], "initialIsOpen": false }, - { - "parentPluginId": "ruleRegistry", - "id": "def-server.createLifecycleExecutor", - "type": "Function", - "tags": [], - "label": "createLifecycleExecutor", - "description": [], - "signature": [ - "(logger: ", - { - "pluginId": "@kbn/logging", - "scope": "common", - "docId": "kibKbnLoggingPluginApi", - "section": "def-common.Logger", - "text": "Logger" - }, - ", ruleDataClient: ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.PublicContract", - "text": "PublicContract" - }, - "<", - { - "pluginId": "ruleRegistry", - "scope": "server", - "docId": "kibRuleRegistryPluginApi", - "section": "def-server.IRuleDataClient", - "text": "IRuleDataClient" - }, - ">) => = never, InstanceContext extends { [x: string]: unknown; } = never, ActionGroupIds extends string = never>(wrappedExecutor: ", - { - "pluginId": "ruleRegistry", - "scope": "server", - "docId": "kibRuleRegistryPluginApi", - "section": "def-server.LifecycleRuleExecutor", - "text": "LifecycleRuleExecutor" - }, - ") => (options: ", - { - "pluginId": "alerting", - "scope": "server", - "docId": "kibAlertingPluginApi", - "section": "def-server.RuleExecutorOptions", - "text": "RuleExecutorOptions" - }, - ", InstanceState, InstanceContext, ActionGroupIds, never>) => Promise<{ state: ", - { - "pluginId": "@kbn/alerting-state-types", - "scope": "server", - "docId": "kibKbnAlertingStateTypesPluginApi", - "section": "def-server.WrappedLifecycleRuleState", - "text": "WrappedLifecycleRuleState" - }, - "; }>" - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "ruleRegistry", - "id": "def-server.createLifecycleExecutor.$1", - "type": "Object", - "tags": [], - "label": "logger", - "description": [], - "signature": [ - { - "pluginId": "@kbn/logging", - "scope": "common", - "docId": "kibKbnLoggingPluginApi", - "section": "def-common.Logger", - "text": "Logger" - } - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "ruleRegistry", - "id": "def-server.createLifecycleExecutor.$2", - "type": "Object", - "tags": [], - "label": "ruleDataClient", - "description": [], - "signature": [ - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.PublicContract", - "text": "PublicContract" - }, - "<", - { - "pluginId": "ruleRegistry", - "scope": "server", - "docId": "kibRuleRegistryPluginApi", - "section": "def-server.IRuleDataClient", - "text": "IRuleDataClient" - }, - ">" - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "ruleRegistry", - "id": "def-server.createLifecycleRuleTypeFactory", - "type": "Function", - "tags": [], - "label": "createLifecycleRuleTypeFactory", - "description": [], - "signature": [ - "({ logger, ruleDataClient }: { logger: ", - { - "pluginId": "@kbn/logging", - "scope": "common", - "docId": "kibKbnLoggingPluginApi", - "section": "def-common.Logger", - "text": "Logger" - }, - "; ruleDataClient: ", - { - "pluginId": "ruleRegistry", - "scope": "server", - "docId": "kibRuleRegistryPluginApi", - "section": "def-server.IRuleDataClient", - "text": "IRuleDataClient" - }, - "; }) => , TAlertInstanceContext extends { [x: string]: unknown; }, TActionGroupIds extends string, TServices extends ", - { - "pluginId": "ruleRegistry", - "scope": "server", - "docId": "kibRuleRegistryPluginApi", - "section": "def-server.LifecycleAlertServices", - "text": "LifecycleAlertServices" - }, - ">(type: ", - { - "pluginId": "ruleRegistry", - "scope": "server", - "docId": "kibRuleRegistryPluginApi", - "section": "def-server.AlertTypeWithExecutor", - "text": "AlertTypeWithExecutor" - }, - ") => ", - { - "pluginId": "ruleRegistry", - "scope": "server", - "docId": "kibRuleRegistryPluginApi", - "section": "def-server.AlertTypeWithExecutor", - "text": "AlertTypeWithExecutor" - }, - "" - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "ruleRegistry", - "id": "def-server.createLifecycleRuleTypeFactory.$1", - "type": "Object", - "tags": [], - "label": "{ logger, ruleDataClient }", - "description": [], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "ruleRegistry", - "id": "def-server.createLifecycleRuleTypeFactory.$1.logger", - "type": "Object", - "tags": [], - "label": "logger", - "description": [], - "signature": [ - { - "pluginId": "@kbn/logging", - "scope": "common", - "docId": "kibKbnLoggingPluginApi", - "section": "def-common.Logger", - "text": "Logger" - } - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "ruleRegistry", - "id": "def-server.createLifecycleRuleTypeFactory.$1.ruleDataClient", - "type": "Object", - "tags": [], - "label": "ruleDataClient", - "description": [], - "signature": [ - { - "pluginId": "ruleRegistry", - "scope": "server", - "docId": "kibRuleRegistryPluginApi", - "section": "def-server.IRuleDataClient", - "text": "IRuleDataClient" - } - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.ts", - "deprecated": false, - "trackAdoption": false - } - ] - } - ], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "ruleRegistry", "id": "def-server.createPersistenceRuleTypeWrapper", @@ -3414,173 +3154,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "ruleRegistry", - "id": "def-server.LifecycleAlertServices", - "type": "Interface", - "tags": [], - "label": "LifecycleAlertServices", - "description": [], - "signature": [ - { - "pluginId": "ruleRegistry", - "scope": "server", - "docId": "kibRuleRegistryPluginApi", - "section": "def-server.LifecycleAlertServices", - "text": "LifecycleAlertServices" - }, - "" - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "ruleRegistry", - "id": "def-server.LifecycleAlertServices.alertWithLifecycle", - "type": "Function", - "tags": [], - "label": "alertWithLifecycle", - "description": [], - "signature": [ - "(alert: { id: string; fields: ExplicitAlertFields; }) => ", - { - "pluginId": "alerting", - "scope": "server", - "docId": "kibAlertingPluginApi", - "section": "def-server.PublicAlert", - "text": "PublicAlert" - }, - "" - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts", - "deprecated": false, - "trackAdoption": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "ruleRegistry", - "id": "def-server.LifecycleAlertServices.alertWithLifecycle.$1", - "type": "Object", - "tags": [], - "label": "alert", - "description": [], - "signature": [ - "{ id: string; fields: ExplicitAlertFields; }" - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "ruleRegistry", - "id": "def-server.LifecycleAlertServices.getAlertStartedDate", - "type": "Function", - "tags": [], - "label": "getAlertStartedDate", - "description": [], - "signature": [ - "(alertInstanceId: string) => string | null" - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "ruleRegistry", - "id": "def-server.LifecycleAlertServices.getAlertStartedDate.$1", - "type": "string", - "tags": [], - "label": "alertInstanceId", - "description": [], - "signature": [ - "string" - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "ruleRegistry", - "id": "def-server.LifecycleAlertServices.getAlertUuid", - "type": "Function", - "tags": [], - "label": "getAlertUuid", - "description": [], - "signature": [ - "(alertInstanceId: string) => string" - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "ruleRegistry", - "id": "def-server.LifecycleAlertServices.getAlertUuid.$1", - "type": "string", - "tags": [], - "label": "alertInstanceId", - "description": [], - "signature": [ - "string" - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "ruleRegistry", - "id": "def-server.LifecycleAlertServices.getAlertByAlertUuid", - "type": "Function", - "tags": [], - "label": "getAlertByAlertUuid", - "description": [], - "signature": [ - "(alertUuid: string) => Promise> & OutputOf>> | null> | null" - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "ruleRegistry", - "id": "def-server.LifecycleAlertServices.getAlertByAlertUuid.$1", - "type": "string", - "tags": [], - "label": "alertUuid", - "description": [], - "signature": [ - "string" - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - } - ], - "initialIsOpen": false - }, { "parentPluginId": "ruleRegistry", "id": "def-server.PersistenceAlertServiceResult", @@ -4265,120 +3838,6 @@ "trackAdoption": false, "initialIsOpen": false }, - { - "parentPluginId": "ruleRegistry", - "id": "def-server.LifecycleAlertService", - "type": "Type", - "tags": [], - "label": "LifecycleAlertService", - "description": [], - "signature": [ - "(alert: { id: string; fields: ExplicitAlertFields; }) => ", - { - "pluginId": "alerting", - "scope": "server", - "docId": "kibAlertingPluginApi", - "section": "def-server.PublicAlert", - "text": "PublicAlert" - }, - "" - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts", - "deprecated": false, - "trackAdoption": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "ruleRegistry", - "id": "def-server.LifecycleAlertService.$1", - "type": "Object", - "tags": [], - "label": "alert", - "description": [], - "signature": [ - "{ id: string; fields: ExplicitAlertFields; }" - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "ruleRegistry", - "id": "def-server.LifecycleRuleExecutor", - "type": "Type", - "tags": [], - "label": "LifecycleRuleExecutor", - "description": [], - "signature": [ - "(options: ", - "AlertExecutorOptionsWithExtraServices", - ">) => Promise<{ state: State; }>" - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts", - "deprecated": false, - "trackAdoption": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "ruleRegistry", - "id": "def-server.LifecycleRuleExecutor.$1", - "type": "CompoundType", - "tags": [], - "label": "options", - "description": [], - "signature": [ - "Omit<", - { - "pluginId": "alerting", - "scope": "server", - "docId": "kibAlertingPluginApi", - "section": "def-server.RuleExecutorOptions", - "text": "RuleExecutorOptions" - }, - ", \"services\"> & { services: ", - { - "pluginId": "alerting", - "scope": "server", - "docId": "kibAlertingPluginApi", - "section": "def-server.RuleExecutorServices", - "text": "RuleExecutorServices" - }, - " & ", - { - "pluginId": "ruleRegistry", - "scope": "server", - "docId": "kibRuleRegistryPluginApi", - "section": "def-server.LifecycleAlertServices", - "text": "LifecycleAlertServices" - }, - "; }" - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "ruleRegistry", "id": "def-server.Mappings", @@ -4848,101 +4307,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "ruleRegistry", - "id": "def-server.RuleRegistryPluginSetupContract.createLifecycleRuleTypeFactory", - "type": "Function", - "tags": [], - "label": "createLifecycleRuleTypeFactory", - "description": [], - "signature": [ - "({ logger, ruleDataClient }: { logger: ", - { - "pluginId": "@kbn/logging", - "scope": "common", - "docId": "kibKbnLoggingPluginApi", - "section": "def-common.Logger", - "text": "Logger" - }, - "; ruleDataClient: ", - { - "pluginId": "ruleRegistry", - "scope": "server", - "docId": "kibRuleRegistryPluginApi", - "section": "def-server.IRuleDataClient", - "text": "IRuleDataClient" - }, - "; }) => , TAlertInstanceContext extends { [x: string]: unknown; }, TActionGroupIds extends string, TServices extends ", - { - "pluginId": "ruleRegistry", - "scope": "server", - "docId": "kibRuleRegistryPluginApi", - "section": "def-server.LifecycleAlertServices", - "text": "LifecycleAlertServices" - }, - ">(type: ", - { - "pluginId": "ruleRegistry", - "scope": "server", - "docId": "kibRuleRegistryPluginApi", - "section": "def-server.AlertTypeWithExecutor", - "text": "AlertTypeWithExecutor" - }, - ") => ", - { - "pluginId": "ruleRegistry", - "scope": "server", - "docId": "kibRuleRegistryPluginApi", - "section": "def-server.AlertTypeWithExecutor", - "text": "AlertTypeWithExecutor" - }, - "" - ], - "path": "x-pack/plugins/rule_registry/server/plugin.ts", - "deprecated": false, - "trackAdoption": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "ruleRegistry", - "id": "def-server.RuleRegistryPluginSetupContract.createLifecycleRuleTypeFactory.$1", - "type": "Object", - "tags": [], - "label": "__0", - "description": [], - "signature": [ - "{ logger: ", - { - "pluginId": "@kbn/logging", - "scope": "common", - "docId": "kibKbnLoggingPluginApi", - "section": "def-common.Logger", - "text": "Logger" - }, - "; ruleDataClient: ", - { - "pluginId": "ruleRegistry", - "scope": "server", - "docId": "kibRuleRegistryPluginApi", - "section": "def-server.IRuleDataClient", - "text": "IRuleDataClient" - }, - "; }" - ], - "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, { "parentPluginId": "ruleRegistry", "id": "def-server.RuleRegistryPluginSetupContract.dataset", diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index aece216c509cb..2b7ea6e299775 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2024-10-29 +date: 2024-11-05 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 | |-------------------|-----------|------------------------|-----------------| -| 285 | 0 | 248 | 11 | +| 263 | 0 | 226 | 10 | ## Server diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index b4fa409a1ed21..048044b0173c3 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2024-10-29 +date: 2024-11-05 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 85676b5a338d4..3370aab608668 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2024-10-29 +date: 2024-11-05 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 0a7ec178cb9a5..652698b8cde9c 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsFinder'] --- import savedObjectsFinderObj from './saved_objects_finder.devdocs.json'; diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index f842a2d62f379..da8681f3c7a4e 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2024-10-29 +date: 2024-11-05 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 4af414f0fd643..b7a69e2cb5b27 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2024-10-29 +date: 2024-11-05 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 80f7dd5d1f873..66b740ba9b2e0 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2024-10-29 +date: 2024-11-05 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 7dba909b3fe86..c85d06300e7e9 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2024-10-29 +date: 2024-11-05 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 abeaf1c896a02..191365e0f7a80 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2024-10-29 +date: 2024-11-05 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 a17e568637578..dc044143c2206 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/search_assistant.mdx b/api_docs/search_assistant.mdx index 94d55df693e8f..2592d69ac2c0b 100644 --- a/api_docs/search_assistant.mdx +++ b/api_docs/search_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchAssistant title: "searchAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the searchAssistant plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchAssistant'] --- import searchAssistantObj from './search_assistant.devdocs.json'; diff --git a/api_docs/search_connectors.mdx b/api_docs/search_connectors.mdx index 50fb8cab753c9..8f64adda47380 100644 --- a/api_docs/search_connectors.mdx +++ b/api_docs/search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchConnectors title: "searchConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the searchConnectors plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchConnectors'] --- import searchConnectorsObj from './search_connectors.devdocs.json'; diff --git a/api_docs/search_homepage.mdx b/api_docs/search_homepage.mdx index a9954afe4df0b..e778a119e4fa2 100644 --- a/api_docs/search_homepage.mdx +++ b/api_docs/search_homepage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchHomepage title: "searchHomepage" image: https://source.unsplash.com/400x175/?github description: API docs for the searchHomepage plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchHomepage'] --- import searchHomepageObj from './search_homepage.devdocs.json'; diff --git a/api_docs/search_indices.devdocs.json b/api_docs/search_indices.devdocs.json index a2fa441a9b27d..2a5d681533d15 100644 --- a/api_docs/search_indices.devdocs.json +++ b/api_docs/search_indices.devdocs.json @@ -85,7 +85,7 @@ "label": "startAppId", "description": [], "signature": [ - "\"fleet\" | \"graph\" | \"ml\" | \"monitoring\" | \"profiling\" | \"metrics\" | \"management\" | \"apm\" | \"synthetics\" | \"ux\" | \"canvas\" | \"logs\" | \"dashboards\" | \"slo\" | \"observabilityAIAssistant\" | \"home\" | \"integrations\" | \"discover\" | \"observability-overview\" | \"appSearch\" | \"dev_tools\" | \"maps\" | \"visualize\" | \"dev_tools:console\" | \"dev_tools:searchprofiler\" | \"dev_tools:painless_lab\" | \"dev_tools:grokdebugger\" | \"ml:notifications\" | \"ml:nodes\" | \"ml:overview\" | \"ml:memoryUsage\" | \"ml:settings\" | \"ml:dataVisualizer\" | \"ml:logPatternAnalysis\" | \"ml:logRateAnalysis\" | \"ml:singleMetricViewer\" | \"ml:anomalyDetection\" | \"ml:anomalyExplorer\" | \"ml:dataDrift\" | \"ml:dataFrameAnalytics\" | \"ml:resultExplorer\" | \"ml:analyticsMap\" | \"ml:aiOps\" | \"ml:changePointDetections\" | \"ml:modelManagement\" | \"ml:nodesOverview\" | \"ml:esqlDataVisualizer\" | \"ml:fileUpload\" | \"ml:indexDataVisualizer\" | \"ml:calendarSettings\" | \"ml:filterListsSettings\" | \"ml:suppliedConfigurations\" | \"osquery\" | \"management:transform\" | \"management:watcher\" | \"management:cases\" | \"management:tags\" | \"management:maintenanceWindows\" | \"management:cross_cluster_replication\" | \"management:dataViews\" | \"management:spaces\" | \"management:settings\" | \"management:users\" | \"management:migrate_data\" | \"management:search_sessions\" | \"management:data_quality\" | \"management:filesManagement\" | \"management:roles\" | \"management:reporting\" | \"management:aiAssistantManagementSelection\" | \"management:securityAiAssistantManagement\" | \"management:observabilityAiAssistantManagement\" | \"management:api_keys\" | \"management:license_management\" | \"management:index_lifecycle_management\" | \"management:index_management\" | \"management:ingest_pipelines\" | \"management:jobsListLink\" | \"management:objects\" | \"management:pipelines\" | \"management:remote_clusters\" | \"management:role_mappings\" | \"management:rollup_jobs\" | \"management:snapshot_restore\" | \"management:triggersActions\" | \"management:triggersActionsConnectors\" | \"management:upgrade_assistant\" | \"enterpriseSearch\" | \"enterpriseSearchContent\" | \"enterpriseSearchApplications\" | \"searchInferenceEndpoints\" | \"enterpriseSearchAnalytics\" | \"workplaceSearch\" | \"serverlessElasticsearch\" | \"serverlessConnectors\" | \"searchPlayground\" | \"searchHomepage\" | \"enterpriseSearchContent:connectors\" | \"enterpriseSearchContent:searchIndices\" | \"enterpriseSearchContent:webCrawlers\" | \"enterpriseSearchApplications:searchApplications\" | \"enterpriseSearchApplications:playground\" | \"appSearch:engines\" | \"searchInferenceEndpoints:inferenceEndpoints\" | \"elasticsearchStart\" | \"elasticsearchIndices\" | \"observability-logs-explorer\" | \"last-used-logs-viewer\" | \"observabilityOnboarding\" | \"inventory\" | \"logs:settings\" | \"logs:stream\" | \"logs:log-categories\" | \"logs:anomalies\" | \"observability-overview:cases\" | \"observability-overview:alerts\" | \"observability-overview:rules\" | \"observability-overview:cases_create\" | \"observability-overview:cases_configure\" | \"metrics:settings\" | \"metrics:hosts\" | \"metrics:inventory\" | \"metrics:metrics-explorer\" | \"metrics:assetDetails\" | \"apm:services\" | \"apm:traces\" | \"apm:dependencies\" | \"apm:service-map\" | \"apm:settings\" | \"apm:service-groups-list\" | \"apm:storage-explorer\" | \"synthetics:overview\" | \"synthetics:certificates\" | \"profiling:functions\" | \"profiling:stacktraces\" | \"profiling:flamegraphs\" | \"inventory:datastreams\" | \"securitySolutionUI\" | \"securitySolutionUI:\" | \"securitySolutionUI:cases\" | \"securitySolutionUI:alerts\" | \"securitySolutionUI:rules\" | \"securitySolutionUI:policy\" | \"securitySolutionUI:overview\" | \"securitySolutionUI:dashboards\" | \"securitySolutionUI:kubernetes\" | \"securitySolutionUI:cases_create\" | \"securitySolutionUI:cases_configure\" | \"securitySolutionUI:hosts\" | \"securitySolutionUI:users\" | \"securitySolutionUI:cloud_defend-policies\" | \"securitySolutionUI:cloud_security_posture-dashboard\" | \"securitySolutionUI:cloud_security_posture-findings\" | \"securitySolutionUI:cloud_security_posture-benchmarks\" | \"securitySolutionUI:network\" | \"securitySolutionUI:data_quality\" | \"securitySolutionUI:explore\" | \"securitySolutionUI:assets\" | \"securitySolutionUI:cloud_defend\" | \"securitySolutionUI:notes\" | \"securitySolutionUI:administration\" | \"securitySolutionUI:attack_discovery\" | \"securitySolutionUI:blocklist\" | \"securitySolutionUI:cloud_security_posture-rules\" | \"securitySolutionUI:detections\" | \"securitySolutionUI:detection_response\" | \"securitySolutionUI:endpoints\" | \"securitySolutionUI:event_filters\" | \"securitySolutionUI:exceptions\" | \"securitySolutionUI:host_isolation_exceptions\" | \"securitySolutionUI:hosts-all\" | \"securitySolutionUI:hosts-anomalies\" | \"securitySolutionUI:hosts-risk\" | \"securitySolutionUI:hosts-events\" | \"securitySolutionUI:hosts-sessions\" | \"securitySolutionUI:hosts-uncommon_processes\" | \"securitySolutionUI:investigations\" | \"securitySolutionUI:get_started\" | \"securitySolutionUI:machine_learning-landing\" | \"securitySolutionUI:network-anomalies\" | \"securitySolutionUI:network-dns\" | \"securitySolutionUI:network-events\" | \"securitySolutionUI:network-flows\" | \"securitySolutionUI:network-http\" | \"securitySolutionUI:network-tls\" | \"securitySolutionUI:response_actions_history\" | \"securitySolutionUI:rules-add\" | \"securitySolutionUI:rules-create\" | \"securitySolutionUI:rules-landing\" | \"securitySolutionUI:threat_intelligence\" | \"securitySolutionUI:timelines\" | \"securitySolutionUI:timelines-templates\" | \"securitySolutionUI:trusted_apps\" | \"securitySolutionUI:users-all\" | \"securitySolutionUI:users-anomalies\" | \"securitySolutionUI:users-authentications\" | \"securitySolutionUI:users-events\" | \"securitySolutionUI:users-risk\" | \"securitySolutionUI:entity_analytics\" | \"securitySolutionUI:entity_analytics-management\" | \"securitySolutionUI:entity_analytics-asset-classification\" | \"securitySolutionUI:entity_analytics-entity_store_management\" | \"securitySolutionUI:coverage-overview\" | \"fleet:settings\" | \"fleet:agents\" | \"fleet:policies\" | \"fleet:data_streams\" | \"fleet:enrollment_tokens\" | \"fleet:uninstall_tokens\"" + "\"fleet\" | \"graph\" | \"ml\" | \"monitoring\" | \"profiling\" | \"metrics\" | \"management\" | \"apm\" | \"synthetics\" | \"ux\" | \"canvas\" | \"logs\" | \"dashboards\" | \"slo\" | \"observabilityAIAssistant\" | \"home\" | \"integrations\" | \"discover\" | \"observability-overview\" | \"appSearch\" | \"dev_tools\" | \"maps\" | \"visualize\" | \"dev_tools:console\" | \"dev_tools:searchprofiler\" | \"dev_tools:painless_lab\" | \"dev_tools:grokdebugger\" | \"ml:notifications\" | \"ml:nodes\" | \"ml:overview\" | \"ml:memoryUsage\" | \"ml:settings\" | \"ml:dataVisualizer\" | \"ml:logPatternAnalysis\" | \"ml:logRateAnalysis\" | \"ml:singleMetricViewer\" | \"ml:anomalyDetection\" | \"ml:anomalyExplorer\" | \"ml:dataDrift\" | \"ml:dataFrameAnalytics\" | \"ml:resultExplorer\" | \"ml:analyticsMap\" | \"ml:aiOps\" | \"ml:changePointDetections\" | \"ml:modelManagement\" | \"ml:nodesOverview\" | \"ml:esqlDataVisualizer\" | \"ml:fileUpload\" | \"ml:indexDataVisualizer\" | \"ml:calendarSettings\" | \"ml:filterListsSettings\" | \"ml:suppliedConfigurations\" | \"osquery\" | \"management:transform\" | \"management:watcher\" | \"management:cases\" | \"management:tags\" | \"management:maintenanceWindows\" | \"management:cross_cluster_replication\" | \"management:dataViews\" | \"management:spaces\" | \"management:settings\" | \"management:users\" | \"management:migrate_data\" | \"management:search_sessions\" | \"management:data_quality\" | \"management:filesManagement\" | \"management:roles\" | \"management:reporting\" | \"management:aiAssistantManagementSelection\" | \"management:securityAiAssistantManagement\" | \"management:observabilityAiAssistantManagement\" | \"management:api_keys\" | \"management:license_management\" | \"management:index_lifecycle_management\" | \"management:index_management\" | \"management:ingest_pipelines\" | \"management:jobsListLink\" | \"management:objects\" | \"management:pipelines\" | \"management:remote_clusters\" | \"management:role_mappings\" | \"management:rollup_jobs\" | \"management:snapshot_restore\" | \"management:triggersActions\" | \"management:triggersActionsConnectors\" | \"management:upgrade_assistant\" | \"enterpriseSearch\" | \"enterpriseSearchContent\" | \"enterpriseSearchApplications\" | \"searchInferenceEndpoints\" | \"enterpriseSearchAnalytics\" | \"workplaceSearch\" | \"serverlessElasticsearch\" | \"serverlessConnectors\" | \"searchPlayground\" | \"searchHomepage\" | \"enterpriseSearchContent:connectors\" | \"enterpriseSearchContent:searchIndices\" | \"enterpriseSearchContent:webCrawlers\" | \"enterpriseSearchApplications:searchApplications\" | \"enterpriseSearchApplications:playground\" | \"appSearch:engines\" | \"searchInferenceEndpoints:inferenceEndpoints\" | \"elasticsearchStart\" | \"elasticsearchIndices\" | \"enterpriseSearchElasticsearch\" | \"enterpriseSearchVectorSearch\" | \"enterpriseSearchSemanticSearch\" | \"enterpriseSearchAISearch\" | \"observability-logs-explorer\" | \"last-used-logs-viewer\" | \"observabilityOnboarding\" | \"inventory\" | \"logs:settings\" | \"logs:stream\" | \"logs:log-categories\" | \"logs:anomalies\" | \"observability-overview:cases\" | \"observability-overview:alerts\" | \"observability-overview:rules\" | \"observability-overview:cases_create\" | \"observability-overview:cases_configure\" | \"metrics:settings\" | \"metrics:hosts\" | \"metrics:inventory\" | \"metrics:metrics-explorer\" | \"metrics:assetDetails\" | \"apm:services\" | \"apm:traces\" | \"apm:dependencies\" | \"apm:service-map\" | \"apm:settings\" | \"apm:service-groups-list\" | \"apm:storage-explorer\" | \"synthetics:overview\" | \"synthetics:certificates\" | \"profiling:functions\" | \"profiling:stacktraces\" | \"profiling:flamegraphs\" | \"inventory:datastreams\" | \"securitySolutionUI\" | \"securitySolutionUI:\" | \"securitySolutionUI:cases\" | \"securitySolutionUI:alerts\" | \"securitySolutionUI:rules\" | \"securitySolutionUI:policy\" | \"securitySolutionUI:overview\" | \"securitySolutionUI:dashboards\" | \"securitySolutionUI:kubernetes\" | \"securitySolutionUI:cases_create\" | \"securitySolutionUI:cases_configure\" | \"securitySolutionUI:hosts\" | \"securitySolutionUI:users\" | \"securitySolutionUI:cloud_defend-policies\" | \"securitySolutionUI:cloud_security_posture-dashboard\" | \"securitySolutionUI:cloud_security_posture-findings\" | \"securitySolutionUI:cloud_security_posture-benchmarks\" | \"securitySolutionUI:network\" | \"securitySolutionUI:data_quality\" | \"securitySolutionUI:explore\" | \"securitySolutionUI:assets\" | \"securitySolutionUI:cloud_defend\" | \"securitySolutionUI:notes\" | \"securitySolutionUI:administration\" | \"securitySolutionUI:attack_discovery\" | \"securitySolutionUI:blocklist\" | \"securitySolutionUI:cloud_security_posture-rules\" | \"securitySolutionUI:detections\" | \"securitySolutionUI:detection_response\" | \"securitySolutionUI:endpoints\" | \"securitySolutionUI:event_filters\" | \"securitySolutionUI:exceptions\" | \"securitySolutionUI:host_isolation_exceptions\" | \"securitySolutionUI:hosts-all\" | \"securitySolutionUI:hosts-anomalies\" | \"securitySolutionUI:hosts-risk\" | \"securitySolutionUI:hosts-events\" | \"securitySolutionUI:hosts-sessions\" | \"securitySolutionUI:hosts-uncommon_processes\" | \"securitySolutionUI:investigations\" | \"securitySolutionUI:get_started\" | \"securitySolutionUI:machine_learning-landing\" | \"securitySolutionUI:network-anomalies\" | \"securitySolutionUI:network-dns\" | \"securitySolutionUI:network-events\" | \"securitySolutionUI:network-flows\" | \"securitySolutionUI:network-http\" | \"securitySolutionUI:network-tls\" | \"securitySolutionUI:response_actions_history\" | \"securitySolutionUI:rules-add\" | \"securitySolutionUI:rules-create\" | \"securitySolutionUI:rules-landing\" | \"securitySolutionUI:threat_intelligence\" | \"securitySolutionUI:timelines\" | \"securitySolutionUI:timelines-templates\" | \"securitySolutionUI:trusted_apps\" | \"securitySolutionUI:users-all\" | \"securitySolutionUI:users-anomalies\" | \"securitySolutionUI:users-authentications\" | \"securitySolutionUI:users-events\" | \"securitySolutionUI:users-risk\" | \"securitySolutionUI:entity_analytics\" | \"securitySolutionUI:entity_analytics-management\" | \"securitySolutionUI:entity_analytics-asset-classification\" | \"securitySolutionUI:entity_analytics-entity_store_management\" | \"securitySolutionUI:coverage-overview\" | \"fleet:settings\" | \"fleet:agents\" | \"fleet:policies\" | \"fleet:data_streams\" | \"fleet:enrollment_tokens\" | \"fleet:uninstall_tokens\"" ], "path": "x-pack/plugins/search_indices/public/types.ts", "deprecated": false, diff --git a/api_docs/search_indices.mdx b/api_docs/search_indices.mdx index f9e6296b62441..207333c7a02b3 100644 --- a/api_docs/search_indices.mdx +++ b/api_docs/search_indices.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchIndices title: "searchIndices" image: https://source.unsplash.com/400x175/?github description: API docs for the searchIndices plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchIndices'] --- import searchIndicesObj from './search_indices.devdocs.json'; diff --git a/api_docs/search_inference_endpoints.mdx b/api_docs/search_inference_endpoints.mdx index 638476d27ad13..53511b8dcd96a 100644 --- a/api_docs/search_inference_endpoints.mdx +++ b/api_docs/search_inference_endpoints.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchInferenceEndpoints title: "searchInferenceEndpoints" image: https://source.unsplash.com/400x175/?github description: API docs for the searchInferenceEndpoints plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchInferenceEndpoints'] --- import searchInferenceEndpointsObj from './search_inference_endpoints.devdocs.json'; diff --git a/api_docs/search_notebooks.mdx b/api_docs/search_notebooks.mdx index 8073d0f6cd8ba..3317d17e682bf 100644 --- a/api_docs/search_notebooks.mdx +++ b/api_docs/search_notebooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchNotebooks title: "searchNotebooks" image: https://source.unsplash.com/400x175/?github description: API docs for the searchNotebooks plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchNotebooks'] --- import searchNotebooksObj from './search_notebooks.devdocs.json'; diff --git a/api_docs/search_playground.mdx b/api_docs/search_playground.mdx index 9de8e31c53e03..0d85aa3fc79b0 100644 --- a/api_docs/search_playground.mdx +++ b/api_docs/search_playground.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchPlayground title: "searchPlayground" image: https://source.unsplash.com/400x175/?github description: API docs for the searchPlayground plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchPlayground'] --- import searchPlaygroundObj from './search_playground.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 7247c07ae0a73..94fb8607494a3 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; diff --git a/api_docs/security_solution.devdocs.json b/api_docs/security_solution.devdocs.json index 275518c9347a9..5da27f3f14753 100644 --- a/api_docs/security_solution.devdocs.json +++ b/api_docs/security_solution.devdocs.json @@ -420,7 +420,7 @@ "\nExperimental flag needed to enable the link" ], "signature": [ - "\"assistantKnowledgeBaseByDefault\" | \"assistantModelEvaluation\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionUploadEnabled\" | \"automatedProcessActionsEnabled\" | \"responseActionsSentinelOneV1Enabled\" | \"responseActionsSentinelOneV2Enabled\" | \"responseActionsSentinelOneGetFileEnabled\" | \"responseActionsSentinelOneKillProcessEnabled\" | \"responseActionsSentinelOneProcessesEnabled\" | \"responseActionsCrowdstrikeManualHostIsolationEnabled\" | \"endpointManagementSpaceAwarenessEnabled\" | \"securitySolutionNotesEnabled\" | \"entityAlertPreviewDisabled\" | \"newUserDetailsFlyoutManagedUser\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"disableTimelineSaveTour\" | \"riskEnginePrivilegesRouteEnabled\" | \"sentinelOneDataInAnalyzerEnabled\" | \"sentinelOneManualHostActionsEnabled\" | \"crowdstrikeDataInAnalyzerEnabled\" | \"responseActionsTelemetryEnabled\" | \"jamfDataInAnalyzerEnabled\" | \"timelineEsqlTabDisabled\" | \"analyzerDatePickersAndSourcererDisabled\" | \"graphVisualizationInFlyoutEnabled\" | \"prebuiltRulesCustomizationEnabled\" | \"malwareOnWriteScanOptionAvailable\" | \"unifiedManifestEnabled\" | \"valueListItemsModalEnabled\" | \"filterProcessDescendantsForEventFiltersEnabled\" | \"dataIngestionHubEnabled\" | \"entityStoreDisabled\" | \"siemMigrationsEnabled\" | undefined" + "\"assistantKnowledgeBaseByDefault\" | \"assistantModelEvaluation\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionUploadEnabled\" | \"automatedProcessActionsEnabled\" | \"responseActionsSentinelOneV1Enabled\" | \"responseActionsSentinelOneV2Enabled\" | \"responseActionsSentinelOneGetFileEnabled\" | \"responseActionsSentinelOneKillProcessEnabled\" | \"responseActionsSentinelOneProcessesEnabled\" | \"responseActionsCrowdstrikeManualHostIsolationEnabled\" | \"endpointManagementSpaceAwarenessEnabled\" | \"securitySolutionNotesDisabled\" | \"entityAlertPreviewDisabled\" | \"newUserDetailsFlyoutManagedUser\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"disableTimelineSaveTour\" | \"riskEnginePrivilegesRouteEnabled\" | \"sentinelOneDataInAnalyzerEnabled\" | \"sentinelOneManualHostActionsEnabled\" | \"crowdstrikeDataInAnalyzerEnabled\" | \"responseActionsTelemetryEnabled\" | \"jamfDataInAnalyzerEnabled\" | \"timelineEsqlTabDisabled\" | \"analyzerDatePickersAndSourcererDisabled\" | \"graphVisualizationInFlyoutEnabled\" | \"prebuiltRulesCustomizationEnabled\" | \"malwareOnWriteScanOptionAvailable\" | \"unifiedManifestEnabled\" | \"valueListItemsModalEnabled\" | \"filterProcessDescendantsForEventFiltersEnabled\" | \"dataIngestionHubEnabled\" | \"entityStoreDisabled\" | \"siemMigrationsEnabled\" | undefined" ], "path": "x-pack/plugins/security_solution/public/common/links/types.ts", "deprecated": false, @@ -500,7 +500,7 @@ "\nExperimental flag needed to disable the link. Opposite of experimentalKey" ], "signature": [ - "\"assistantKnowledgeBaseByDefault\" | \"assistantModelEvaluation\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionUploadEnabled\" | \"automatedProcessActionsEnabled\" | \"responseActionsSentinelOneV1Enabled\" | \"responseActionsSentinelOneV2Enabled\" | \"responseActionsSentinelOneGetFileEnabled\" | \"responseActionsSentinelOneKillProcessEnabled\" | \"responseActionsSentinelOneProcessesEnabled\" | \"responseActionsCrowdstrikeManualHostIsolationEnabled\" | \"endpointManagementSpaceAwarenessEnabled\" | \"securitySolutionNotesEnabled\" | \"entityAlertPreviewDisabled\" | \"newUserDetailsFlyoutManagedUser\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"disableTimelineSaveTour\" | \"riskEnginePrivilegesRouteEnabled\" | \"sentinelOneDataInAnalyzerEnabled\" | \"sentinelOneManualHostActionsEnabled\" | \"crowdstrikeDataInAnalyzerEnabled\" | \"responseActionsTelemetryEnabled\" | \"jamfDataInAnalyzerEnabled\" | \"timelineEsqlTabDisabled\" | \"analyzerDatePickersAndSourcererDisabled\" | \"graphVisualizationInFlyoutEnabled\" | \"prebuiltRulesCustomizationEnabled\" | \"malwareOnWriteScanOptionAvailable\" | \"unifiedManifestEnabled\" | \"valueListItemsModalEnabled\" | \"filterProcessDescendantsForEventFiltersEnabled\" | \"dataIngestionHubEnabled\" | \"entityStoreDisabled\" | \"siemMigrationsEnabled\" | undefined" + "\"assistantKnowledgeBaseByDefault\" | \"assistantModelEvaluation\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionUploadEnabled\" | \"automatedProcessActionsEnabled\" | \"responseActionsSentinelOneV1Enabled\" | \"responseActionsSentinelOneV2Enabled\" | \"responseActionsSentinelOneGetFileEnabled\" | \"responseActionsSentinelOneKillProcessEnabled\" | \"responseActionsSentinelOneProcessesEnabled\" | \"responseActionsCrowdstrikeManualHostIsolationEnabled\" | \"endpointManagementSpaceAwarenessEnabled\" | \"securitySolutionNotesDisabled\" | \"entityAlertPreviewDisabled\" | \"newUserDetailsFlyoutManagedUser\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"disableTimelineSaveTour\" | \"riskEnginePrivilegesRouteEnabled\" | \"sentinelOneDataInAnalyzerEnabled\" | \"sentinelOneManualHostActionsEnabled\" | \"crowdstrikeDataInAnalyzerEnabled\" | \"responseActionsTelemetryEnabled\" | \"jamfDataInAnalyzerEnabled\" | \"timelineEsqlTabDisabled\" | \"analyzerDatePickersAndSourcererDisabled\" | \"graphVisualizationInFlyoutEnabled\" | \"prebuiltRulesCustomizationEnabled\" | \"malwareOnWriteScanOptionAvailable\" | \"unifiedManifestEnabled\" | \"valueListItemsModalEnabled\" | \"filterProcessDescendantsForEventFiltersEnabled\" | \"dataIngestionHubEnabled\" | \"entityStoreDisabled\" | \"siemMigrationsEnabled\" | undefined" ], "path": "x-pack/plugins/security_solution/public/common/links/types.ts", "deprecated": false, @@ -1791,7 +1791,7 @@ "label": "experimentalFeatures", "description": [], "signature": [ - "{ readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly responseActionsSentinelOneV2Enabled: boolean; readonly responseActionsSentinelOneGetFileEnabled: boolean; readonly responseActionsSentinelOneKillProcessEnabled: boolean; readonly responseActionsSentinelOneProcessesEnabled: boolean; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: boolean; readonly endpointManagementSpaceAwarenessEnabled: boolean; readonly securitySolutionNotesEnabled: boolean; readonly entityAlertPreviewDisabled: boolean; readonly assistantModelEvaluation: boolean; readonly assistantKnowledgeBaseByDefault: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly crowdstrikeDataInAnalyzerEnabled: boolean; readonly responseActionsTelemetryEnabled: boolean; readonly jamfDataInAnalyzerEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; readonly analyzerDatePickersAndSourcererDisabled: boolean; readonly graphVisualizationInFlyoutEnabled: boolean; readonly prebuiltRulesCustomizationEnabled: boolean; readonly malwareOnWriteScanOptionAvailable: boolean; readonly unifiedManifestEnabled: boolean; readonly valueListItemsModalEnabled: boolean; readonly filterProcessDescendantsForEventFiltersEnabled: boolean; readonly dataIngestionHubEnabled: boolean; readonly entityStoreDisabled: boolean; readonly siemMigrationsEnabled: boolean; }" + "{ readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly responseActionsSentinelOneV2Enabled: boolean; readonly responseActionsSentinelOneGetFileEnabled: boolean; readonly responseActionsSentinelOneKillProcessEnabled: boolean; readonly responseActionsSentinelOneProcessesEnabled: boolean; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: boolean; readonly endpointManagementSpaceAwarenessEnabled: boolean; readonly securitySolutionNotesDisabled: boolean; readonly entityAlertPreviewDisabled: boolean; readonly assistantModelEvaluation: boolean; readonly assistantKnowledgeBaseByDefault: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly crowdstrikeDataInAnalyzerEnabled: boolean; readonly responseActionsTelemetryEnabled: boolean; readonly jamfDataInAnalyzerEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; readonly analyzerDatePickersAndSourcererDisabled: boolean; readonly graphVisualizationInFlyoutEnabled: boolean; readonly prebuiltRulesCustomizationEnabled: boolean; readonly malwareOnWriteScanOptionAvailable: boolean; readonly unifiedManifestEnabled: boolean; readonly valueListItemsModalEnabled: boolean; readonly filterProcessDescendantsForEventFiltersEnabled: boolean; readonly dataIngestionHubEnabled: boolean; readonly entityStoreDisabled: boolean; readonly siemMigrationsEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/public/types.ts", "deprecated": false, @@ -2918,7 +2918,7 @@ "label": "ConfigType", "description": [], "signature": [ - "Omit; entityAnalytics: Readonly<{} & { riskEngine: Readonly<{} & { alertSampleSizePerShard: number; }>; assetCriticality: Readonly<{} & { csvUpload: Readonly<{} & { errorRetries: number; maxBulkRequestBodySizeBytes: number; }>; }>; entityStore: Readonly<{} & { developer: Readonly<{} & { pipelineDebugMode: boolean; }>; }>; }>; }>, \"offeringSettings\"> & { experimentalFeatures: ", + "Omit; entityAnalytics: Readonly<{} & { riskEngine: Readonly<{} & { alertSampleSizePerShard: number; }>; assetCriticality: Readonly<{} & { csvUpload: Readonly<{} & { errorRetries: number; maxBulkRequestBodySizeBytes: number; }>; }>; entityStore: Readonly<{} & { frequency: moment.Duration; syncDelay: moment.Duration; developer: Readonly<{} & { pipelineDebugMode: boolean; }>; }>; }>; }>, \"offeringSettings\"> & { experimentalFeatures: ", { "pluginId": "securitySolution", "scope": "common", @@ -2993,7 +2993,7 @@ "\nThe security solution generic experimental features" ], "signature": [ - "{ readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly responseActionsSentinelOneV2Enabled: boolean; readonly responseActionsSentinelOneGetFileEnabled: boolean; readonly responseActionsSentinelOneKillProcessEnabled: boolean; readonly responseActionsSentinelOneProcessesEnabled: boolean; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: boolean; readonly endpointManagementSpaceAwarenessEnabled: boolean; readonly securitySolutionNotesEnabled: boolean; readonly entityAlertPreviewDisabled: boolean; readonly assistantModelEvaluation: boolean; readonly assistantKnowledgeBaseByDefault: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly crowdstrikeDataInAnalyzerEnabled: boolean; readonly responseActionsTelemetryEnabled: boolean; readonly jamfDataInAnalyzerEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; readonly analyzerDatePickersAndSourcererDisabled: boolean; readonly graphVisualizationInFlyoutEnabled: boolean; readonly prebuiltRulesCustomizationEnabled: boolean; readonly malwareOnWriteScanOptionAvailable: boolean; readonly unifiedManifestEnabled: boolean; readonly valueListItemsModalEnabled: boolean; readonly filterProcessDescendantsForEventFiltersEnabled: boolean; readonly dataIngestionHubEnabled: boolean; readonly entityStoreDisabled: boolean; readonly siemMigrationsEnabled: boolean; }" + "{ readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly responseActionsSentinelOneV2Enabled: boolean; readonly responseActionsSentinelOneGetFileEnabled: boolean; readonly responseActionsSentinelOneKillProcessEnabled: boolean; readonly responseActionsSentinelOneProcessesEnabled: boolean; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: boolean; readonly endpointManagementSpaceAwarenessEnabled: boolean; readonly securitySolutionNotesDisabled: boolean; readonly entityAlertPreviewDisabled: boolean; readonly assistantModelEvaluation: boolean; readonly assistantKnowledgeBaseByDefault: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly crowdstrikeDataInAnalyzerEnabled: boolean; readonly responseActionsTelemetryEnabled: boolean; readonly jamfDataInAnalyzerEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; readonly analyzerDatePickersAndSourcererDisabled: boolean; readonly graphVisualizationInFlyoutEnabled: boolean; readonly prebuiltRulesCustomizationEnabled: boolean; readonly malwareOnWriteScanOptionAvailable: boolean; readonly unifiedManifestEnabled: boolean; readonly valueListItemsModalEnabled: boolean; readonly filterProcessDescendantsForEventFiltersEnabled: boolean; readonly dataIngestionHubEnabled: boolean; readonly entityStoreDisabled: boolean; readonly siemMigrationsEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/server/plugin_contract.ts", "deprecated": false, @@ -3166,7 +3166,7 @@ "label": "ExperimentalFeatures", "description": [], "signature": [ - "{ readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly responseActionsSentinelOneV2Enabled: boolean; readonly responseActionsSentinelOneGetFileEnabled: boolean; readonly responseActionsSentinelOneKillProcessEnabled: boolean; readonly responseActionsSentinelOneProcessesEnabled: boolean; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: boolean; readonly endpointManagementSpaceAwarenessEnabled: boolean; readonly securitySolutionNotesEnabled: boolean; readonly entityAlertPreviewDisabled: boolean; readonly assistantModelEvaluation: boolean; readonly assistantKnowledgeBaseByDefault: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly crowdstrikeDataInAnalyzerEnabled: boolean; readonly responseActionsTelemetryEnabled: boolean; readonly jamfDataInAnalyzerEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; readonly analyzerDatePickersAndSourcererDisabled: boolean; readonly graphVisualizationInFlyoutEnabled: boolean; readonly prebuiltRulesCustomizationEnabled: boolean; readonly malwareOnWriteScanOptionAvailable: boolean; readonly unifiedManifestEnabled: boolean; readonly valueListItemsModalEnabled: boolean; readonly filterProcessDescendantsForEventFiltersEnabled: boolean; readonly dataIngestionHubEnabled: boolean; readonly entityStoreDisabled: boolean; readonly siemMigrationsEnabled: boolean; }" + "{ readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly responseActionsSentinelOneV2Enabled: boolean; readonly responseActionsSentinelOneGetFileEnabled: boolean; readonly responseActionsSentinelOneKillProcessEnabled: boolean; readonly responseActionsSentinelOneProcessesEnabled: boolean; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: boolean; readonly endpointManagementSpaceAwarenessEnabled: boolean; readonly securitySolutionNotesDisabled: boolean; readonly entityAlertPreviewDisabled: boolean; readonly assistantModelEvaluation: boolean; readonly assistantKnowledgeBaseByDefault: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly crowdstrikeDataInAnalyzerEnabled: boolean; readonly responseActionsTelemetryEnabled: boolean; readonly jamfDataInAnalyzerEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; readonly analyzerDatePickersAndSourcererDisabled: boolean; readonly graphVisualizationInFlyoutEnabled: boolean; readonly prebuiltRulesCustomizationEnabled: boolean; readonly malwareOnWriteScanOptionAvailable: boolean; readonly unifiedManifestEnabled: boolean; readonly valueListItemsModalEnabled: boolean; readonly filterProcessDescendantsForEventFiltersEnabled: boolean; readonly dataIngestionHubEnabled: boolean; readonly entityStoreDisabled: boolean; readonly siemMigrationsEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/common/experimental_features.ts", "deprecated": false, @@ -3232,7 +3232,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 excludePoliciesInFilterEnabled: false; readonly kubernetesEnabled: true; readonly donutChartEmbeddablesEnabled: false; readonly previewTelemetryUrlEnabled: false; readonly extendedRuleExecutionLoggingEnabled: false; readonly socTrendsEnabled: false; readonly responseActionUploadEnabled: true; readonly automatedProcessActionsEnabled: true; readonly responseActionsSentinelOneV1Enabled: true; readonly responseActionsSentinelOneV2Enabled: true; readonly responseActionsSentinelOneGetFileEnabled: true; readonly responseActionsSentinelOneKillProcessEnabled: true; readonly responseActionsSentinelOneProcessesEnabled: true; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: true; readonly endpointManagementSpaceAwarenessEnabled: false; readonly securitySolutionNotesEnabled: false; readonly entityAlertPreviewDisabled: false; readonly assistantModelEvaluation: false; readonly assistantKnowledgeBaseByDefault: false; readonly newUserDetailsFlyoutManagedUser: false; readonly riskScoringPersistence: true; readonly riskScoringRoutesEnabled: true; readonly esqlRulesDisabled: false; readonly protectionUpdatesEnabled: true; readonly disableTimelineSaveTour: false; readonly riskEnginePrivilegesRouteEnabled: true; readonly sentinelOneDataInAnalyzerEnabled: true; readonly sentinelOneManualHostActionsEnabled: true; readonly crowdstrikeDataInAnalyzerEnabled: true; readonly responseActionsTelemetryEnabled: false; readonly jamfDataInAnalyzerEnabled: true; readonly timelineEsqlTabDisabled: false; readonly analyzerDatePickersAndSourcererDisabled: false; readonly graphVisualizationInFlyoutEnabled: false; readonly prebuiltRulesCustomizationEnabled: false; readonly malwareOnWriteScanOptionAvailable: true; readonly unifiedManifestEnabled: true; readonly valueListItemsModalEnabled: true; readonly filterProcessDescendantsForEventFiltersEnabled: true; readonly dataIngestionHubEnabled: false; readonly entityStoreDisabled: false; readonly siemMigrationsEnabled: false; }" + "{ readonly excludePoliciesInFilterEnabled: false; readonly kubernetesEnabled: true; readonly donutChartEmbeddablesEnabled: false; readonly previewTelemetryUrlEnabled: false; readonly extendedRuleExecutionLoggingEnabled: false; readonly socTrendsEnabled: false; readonly responseActionUploadEnabled: true; readonly automatedProcessActionsEnabled: true; readonly responseActionsSentinelOneV1Enabled: true; readonly responseActionsSentinelOneV2Enabled: true; readonly responseActionsSentinelOneGetFileEnabled: true; readonly responseActionsSentinelOneKillProcessEnabled: true; readonly responseActionsSentinelOneProcessesEnabled: true; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: true; readonly endpointManagementSpaceAwarenessEnabled: false; readonly securitySolutionNotesDisabled: false; readonly entityAlertPreviewDisabled: false; readonly assistantModelEvaluation: false; readonly assistantKnowledgeBaseByDefault: true; readonly newUserDetailsFlyoutManagedUser: false; readonly riskScoringPersistence: true; readonly riskScoringRoutesEnabled: true; readonly esqlRulesDisabled: false; readonly protectionUpdatesEnabled: true; readonly disableTimelineSaveTour: false; readonly riskEnginePrivilegesRouteEnabled: true; readonly sentinelOneDataInAnalyzerEnabled: true; readonly sentinelOneManualHostActionsEnabled: true; readonly crowdstrikeDataInAnalyzerEnabled: true; readonly responseActionsTelemetryEnabled: false; readonly jamfDataInAnalyzerEnabled: true; readonly timelineEsqlTabDisabled: false; readonly analyzerDatePickersAndSourcererDisabled: false; readonly graphVisualizationInFlyoutEnabled: false; readonly prebuiltRulesCustomizationEnabled: false; readonly malwareOnWriteScanOptionAvailable: true; readonly unifiedManifestEnabled: true; readonly valueListItemsModalEnabled: true; readonly filterProcessDescendantsForEventFiltersEnabled: true; readonly dataIngestionHubEnabled: false; readonly entityStoreDisabled: false; readonly siemMigrationsEnabled: false; }" ], "path": "x-pack/plugins/security_solution/common/experimental_features.ts", "deprecated": false, diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index 07363049df384..4c102826ec973 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/security_solution_ess.mdx b/api_docs/security_solution_ess.mdx index 214913c6ee972..31326e62344ae 100644 --- a/api_docs/security_solution_ess.mdx +++ b/api_docs/security_solution_ess.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionEss title: "securitySolutionEss" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionEss plugin -date: 2024-10-29 +date: 2024-11-05 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 56660f9e739b6..adb6ecab7eadc 100644 --- a/api_docs/security_solution_serverless.mdx +++ b/api_docs/security_solution_serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionServerless title: "securitySolutionServerless" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionServerless plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionServerless'] --- import securitySolutionServerlessObj from './security_solution_serverless.devdocs.json'; diff --git a/api_docs/serverless.devdocs.json b/api_docs/serverless.devdocs.json index 110cf72e9d79e..5ff67d3104e84 100644 --- a/api_docs/serverless.devdocs.json +++ b/api_docs/serverless.devdocs.json @@ -166,7 +166,15 @@ "label": "initNavigation", "description": [], "signature": [ - "(id: string, navigationTree$: ", + "(id: ", + { + "pluginId": "@kbn/core-chrome-browser", + "scope": "public", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-public.SolutionId", + "text": "SolutionId" + }, + ", navigationTree$: ", "Observable", "<", { @@ -201,12 +209,18 @@ { "parentPluginId": "serverless", "id": "def-public.ServerlessPluginStart.initNavigation.$1", - "type": "string", + "type": "CompoundType", "tags": [], "label": "id", "description": [], "signature": [ - "string" + { + "pluginId": "@kbn/core-chrome-browser", + "scope": "public", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-public.SolutionId", + "text": "SolutionId" + } ], "path": "x-pack/plugins/serverless/public/types.ts", "deprecated": false, diff --git a/api_docs/serverless.mdx b/api_docs/serverless.mdx index 440495f90bccb..e7ec8d251dbf6 100644 --- a/api_docs/serverless.mdx +++ b/api_docs/serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverless title: "serverless" image: https://source.unsplash.com/400x175/?github description: API docs for the serverless plugin -date: 2024-10-29 +date: 2024-11-05 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 46fd76d3c7a46..bc3650892a6e0 100644 --- a/api_docs/serverless_observability.mdx +++ b/api_docs/serverless_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessObservability title: "serverlessObservability" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessObservability plugin -date: 2024-10-29 +date: 2024-11-05 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 2be504bf78571..9433b63f12947 100644 --- a/api_docs/serverless_search.mdx +++ b/api_docs/serverless_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessSearch title: "serverlessSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessSearch plugin -date: 2024-10-29 +date: 2024-11-05 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 b6ef4bb0426bc..b17d6e1f721f4 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView'] --- import sessionViewObj from './session_view.devdocs.json'; diff --git a/api_docs/share.devdocs.json b/api_docs/share.devdocs.json index 779c2a12f4c64..f059f4827715a 100644 --- a/api_docs/share.devdocs.json +++ b/api_docs/share.devdocs.json @@ -905,7 +905,7 @@ }, { "plugin": "discover", - "path": "src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx" + "path": "src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_share.tsx" } ] }, diff --git a/api_docs/share.mdx b/api_docs/share.mdx index 105bb47b0e6aa..9a9fa031a321a 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/slo.mdx b/api_docs/slo.mdx index 35fda9d029861..337c0e31d744b 100644 --- a/api_docs/slo.mdx +++ b/api_docs/slo.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/slo title: "slo" image: https://source.unsplash.com/400x175/?github description: API docs for the slo plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'slo'] --- import sloObj from './slo.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 29cd041453c9f..bc416c95cdf81 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore'] --- import snapshotRestoreObj from './snapshot_restore.devdocs.json'; diff --git a/api_docs/spaces.devdocs.json b/api_docs/spaces.devdocs.json index e87c7b38031a0..93ea371a06d61 100644 --- a/api_docs/spaces.devdocs.json +++ b/api_docs/spaces.devdocs.json @@ -5154,11 +5154,11 @@ "description": [], "signature": [ { - "pluginId": "cloud", - "scope": "common", - "docId": "kibCloudPluginApi", - "section": "def-common.OnBoardingDefaultSolution", - "text": "OnBoardingDefaultSolution" + "pluginId": "@kbn/core-chrome-browser", + "scope": "public", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-public.SolutionId", + "text": "SolutionId" }, " | \"classic\"" ], diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index 38f16f9689e23..8057aae0d25a5 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2024-10-29 +date: 2024-11-05 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 de98035b56586..bba6a2143e503 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2024-10-29 +date: 2024-11-05 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 d158ef80add1d..fe1e633fda40e 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2024-10-29 +date: 2024-11-05 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 8ac5f33af1066..0c84153447ed0 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2024-10-29 +date: 2024-11-05 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 cc69d570e86ef..9f31699831028 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2024-10-29 +date: 2024-11-05 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 389a9b5546e1f..54a1325820fcf 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2024-10-29 +date: 2024-11-05 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 1f738be296108..9d4b58a3b2f5c 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2024-10-29 +date: 2024-11-05 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 7f140b3f89533..90bb8a545417e 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index a7fac2c78449e..adaa8c3b7b116 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2024-10-29 +date: 2024-11-05 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 9c3be393bfb02..1e9950ae466c1 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2024-10-29 +date: 2024-11-05 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 cf2886680f4c2..5d42001e70860 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2024-10-29 +date: 2024-11-05 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 cd9ab1db37678..7dd56991798c1 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index f3f746140b3e4..56776329486cf 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; diff --git a/api_docs/ui_actions_enhanced.devdocs.json b/api_docs/ui_actions_enhanced.devdocs.json index 16230d3a4fce8..8c5425102a713 100644 --- a/api_docs/ui_actions_enhanced.devdocs.json +++ b/api_docs/ui_actions_enhanced.devdocs.json @@ -2274,7 +2274,7 @@ "label": "validateUrl", "description": [], "signature": [ - "(url: string) => { isValid: boolean; error?: string | undefined; }" + "(url: string) => { isValid: boolean; error?: string | undefined; invalidUrl?: string | undefined; }" ], "path": "src/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/url_validation.ts", "deprecated": false, @@ -2315,7 +2315,7 @@ "section": "def-public.UrlDrilldownScope", "text": "UrlDrilldownScope" }, - ") => Promise<{ isValid: boolean; error?: string | undefined; }>" + ") => Promise<{ isValid: boolean; error?: string | undefined; invalidUrl?: string | undefined; }>" ], "path": "src/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/url_validation.ts", "deprecated": false, diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index cc6a40541f673..72d043fa0249d 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2024-10-29 +date: 2024-11-05 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 607f30a503af0..1eceab991425f 100644 --- a/api_docs/unified_doc_viewer.mdx +++ b/api_docs/unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedDocViewer title: "unifiedDocViewer" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedDocViewer plugin -date: 2024-10-29 +date: 2024-11-05 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 9444fb3fc24f5..ca0012c50e155 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index dd3c05f389fc3..4d7b77a1c3574 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2024-10-29 +date: 2024-11-05 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 0464a486eefe1..916801660e1f3 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2024-10-29 +date: 2024-11-05 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 25e8b52249f51..1baa84a2bf05f 100644 --- a/api_docs/uptime.mdx +++ b/api_docs/uptime.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uptime title: "uptime" image: https://source.unsplash.com/400x175/?github description: API docs for the uptime plugin -date: 2024-10-29 +date: 2024-11-05 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 a231f10fe5fd9..23196e765a82a 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2024-10-29 +date: 2024-11-05 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 70a62e3163e51..a7aa2440a30ce 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2024-10-29 +date: 2024-11-05 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 e051c92d227a7..612908d626e63 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2024-10-29 +date: 2024-11-05 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 d48a74c2a5db8..aef778560f240 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2024-10-29 +date: 2024-11-05 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 cd1acb2a34f1f..da6269b2ff101 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2024-10-29 +date: 2024-11-05 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 fc5ccee769b45..76ea3f1061020 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2024-10-29 +date: 2024-11-05 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 ad5b20ace0a5e..22e1a95c27325 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2024-10-29 +date: 2024-11-05 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 23c2b8139e2e3..77472233bd03b 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2024-10-29 +date: 2024-11-05 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 561487fc985bb..7697dde509e43 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2024-10-29 +date: 2024-11-05 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 44e78cec00a59..4130f0d01b5f6 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2024-10-29 +date: 2024-11-05 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 61814f4fe1e79..c35c2030f9c8e 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2024-10-29 +date: 2024-11-05 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 4ac611c78c136..de07fa4273a21 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2024-10-29 +date: 2024-11-05 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 7aa02deae55c1..3aabe341d6f26 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 111d20c3ec900..a900a3034c098 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2024-10-29 +date: 2024-11-05 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/config/kibana.yml b/config/kibana.yml index 6c4fb774eb37c..c816337f881d4 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -181,8 +181,3 @@ # Maximum number of documents loaded by each shard to generate autocomplete suggestions. # This value must be a whole number greater than zero. Defaults to 100_000 #unifiedSearch.autocomplete.valueSuggestions.terminateAfter: 100000 - -# Must be removed before v9 release -# Requires all registry packages to add v9 as a compatible semver range -# https://github.com/elastic/kibana/issues/192624 -xpack.fleet.internal.registry.kibanaVersionCheckEnabled: false diff --git a/dev_docs/key_concepts/api_authorization.mdx b/dev_docs/key_concepts/api_authorization.mdx index b781808757c9a..cda6ad5de21ce 100644 --- a/dev_docs/key_concepts/api_authorization.mdx +++ b/dev_docs/key_concepts/api_authorization.mdx @@ -313,6 +313,23 @@ Routes without a compelling reason to opt-out of authorization should plan to in MIGRATE_DISABLED_AUTHZ=true MIGRATE_ENABLED_AUTHZ=true npx eslint --ext .ts --fix path/to/your/folder ``` +**How to migrate if you have an utility function for route creation?** +If you have utility function that creates routes, i.e `createApmServerRoute` or `createObservabilityOnboardingServerRoute`, you can easily modify the eslint rule to handle your case. +For example, you register the route with `access` tags in your utility function: +```ts +createApmServerRoute({ + endpoint: 'GET /your/route/path', + options: { tags: ['access:apm'] }, + handler: async (resources): => { + // your handler logic + }, +}) +``` +You can modify [the rule](https://github.com/elastic/kibana/blob/6a50066e00ae38a64c5365fd66b4dc32857ba1fc/packages/kbn-eslint-plugin-eslint/rules/no_deprecated_authz_config.js#L312-#L315) to handle your case by adding the following code: +```ts +callee.type === 'Identifier' && callee.name === 'createApmServerRoute' +``` + ## Questions? If you have any questions or need help with API authorization, please reach out to the `@elastic/kibana-security` team. diff --git a/dev_docs/nav-kibana-dev.docnav.json b/dev_docs/nav-kibana-dev.docnav.json index 6dd2ca052b7bd..289593aaf159c 100644 --- a/dev_docs/nav-kibana-dev.docnav.json +++ b/dev_docs/nav-kibana-dev.docnav.json @@ -183,8 +183,13 @@ "label": "data.search" }, { - "id": "kibDevTutorialFileService", - "label": "File service" + "id": "kibDevTutorialScreenshotting", + "label": "Screenshotting", + "items": [ + { + "id": "kibDevDocsUpdatingPuppeteerAndChromium" + } + ] }, { "id": "kibDevTutorialDataViews" @@ -266,10 +271,6 @@ } ] }, - { - "id": "kibDevTutorialScreenshotting", - "label": "Screenshotting" - }, { "id": "kibDevTutorialAdvancedSettings", "label": "Advanced Settings" diff --git a/dev_docs/shared_ux/shared_ux_landing.mdx b/dev_docs/shared_ux/shared_ux_landing.mdx index 6093b1c5c943f..d5cebf7b76b1f 100644 --- a/dev_docs/shared_ux/shared_ux_landing.mdx +++ b/dev_docs/shared_ux/shared_ux_landing.mdx @@ -46,16 +46,6 @@ layout: landing pageId: 'kibContentManagement', description: 'Learn about the content management system in Kibana', }, - { - pageId: 'kibDevTutorialScreenshotting', - title: 'Reporting / Screenshotting', - description: 'Learn how to integrate your plugin with reporting', - }, - { - pageId: 'kibDevDocsUpdatingPuppeteerAndChromium', - title: 'Reporting / Updating Puppeteer and Chromium', - description: 'Learn how to update the Puppeteer node module and build headless Chromium', - }, { pageId: 'kibDevTutorialAdvancedSettings', title: 'Advanced Settings (uiSettings)', diff --git a/dev_docs/shared_ux/browser_snapshots_filter1.png b/dev_docs/tutorials/screenshotting/browser_snapshots_filter1.png similarity index 100% rename from dev_docs/shared_ux/browser_snapshots_filter1.png rename to dev_docs/tutorials/screenshotting/browser_snapshots_filter1.png diff --git a/dev_docs/shared_ux/browser_snapshots_filter2.png b/dev_docs/tutorials/screenshotting/browser_snapshots_filter2.png similarity index 100% rename from dev_docs/shared_ux/browser_snapshots_filter2.png rename to dev_docs/tutorials/screenshotting/browser_snapshots_filter2.png diff --git a/dev_docs/shared_ux/browser_snapshots_listing.png b/dev_docs/tutorials/screenshotting/browser_snapshots_listing.png similarity index 100% rename from dev_docs/shared_ux/browser_snapshots_listing.png rename to dev_docs/tutorials/screenshotting/browser_snapshots_listing.png diff --git a/dev_docs/shared_ux/chromium_version_command.png b/dev_docs/tutorials/screenshotting/chromium_version_command.png similarity index 100% rename from dev_docs/shared_ux/chromium_version_command.png rename to dev_docs/tutorials/screenshotting/chromium_version_command.png diff --git a/dev_docs/tutorials/screenshotting.mdx b/dev_docs/tutorials/screenshotting/screenshotting.mdx similarity index 100% rename from dev_docs/tutorials/screenshotting.mdx rename to dev_docs/tutorials/screenshotting/screenshotting.mdx diff --git a/dev_docs/shared_ux/updating_puppeteer_and_chromium.mdx b/dev_docs/tutorials/screenshotting/screenshotting_updating_puppeteer_and_chromium.mdx similarity index 100% rename from dev_docs/shared_ux/updating_puppeteer_and_chromium.mdx rename to dev_docs/tutorials/screenshotting/screenshotting_updating_puppeteer_and_chromium.mdx diff --git a/docs/CHANGELOG.asciidoc b/docs/CHANGELOG.asciidoc index 074dc6ffd05f0..3c102cbbf9384 100644 --- a/docs/CHANGELOG.asciidoc +++ b/docs/CHANGELOG.asciidoc @@ -10,6 +10,7 @@ Review important information about the {kib} 8.x releases. +* <> * <> * <> * <> @@ -77,6 +78,360 @@ Review important information about the {kib} 8.x releases. include::upgrade-notes.asciidoc[] + +[[release-notes-8.16.0]] +== {kib} 8.16.0 + +For information about the {kib} 8.16.0 release, review the following information. + +[float] +[[deprecations-8.16.0]] +=== Deprecations + +The following functionality is deprecated in 8.16.0, and will be removed in 9.0.0. +Deprecated functionality does not have an immediate impact on your application, but we strongly recommend +you make the necessary updates after you upgrade to 8.16.0. + +[discrete] +* The Logs Stream is now hidden by default in favor of the Logs Explorer app. +[%collapsible] +==== +*Details* + +You can find the Logs Explorer app in the navigation menu under Logs > Explorer, or as a separate tab in Discover. For more information, refer to ({kibana-pull}194519[#194519]). + +*Impact* + +You can still show the Logs Stream app again by navigating to Stack Management > Advanced Settings and by enabling the `observability:enableLogsStream` setting. +==== + +[discrete] +* Deprecates the Observability AI Assistant specific advanced setting `observability:aiAssistantLogsIndexPattern`. +[%collapsible] +==== +*Details* + +The Observability AI Assistant specific advanced setting for Logs index patterns `observability:aiAssistantLogsIndexPattern` is deprecated and no longer used. The AI Assistant will now use the existing **Log sources** setting `observability:logSources` instead. For more information, refer to ({kibana-pull}192003[#192003]). + +//*Impact* + +//!!TODO!! +==== + + + +[float] +[[features-8.16.0]] +=== Features +{kib} 8.16.0 adds the following new and notable features. + +AGPL license:: +* Adds AGPL 3.0 license ({kibana-pull}192025[#192025]). +Alerting:: +* Adds TheHive connector ({kibana-pull}180138[#180138]). +* Adds flapping settings per rule ({kibana-pull}189341[#189341]). +Cases:: +* Support TheHive connector in cases ({kibana-pull}180931[#180931]). +Dashboards and visualizations:: +* Adds the ability to star your favorite dashboards and quickly find them ({kibana-pull}189285[#189285]). +* Adds a chart showing usage statistics to the dashboard details ({kibana-pull}187993[#187993]). +* Adds metric styling options in *Lens* ({kibana-pull}186929[#186929]). +* Adds support for coloring table cells by terms with color mappings assignments. This is supported for both Rows and Metric dimensions ({kibana-pull}189895[#189895]). +Data ingestion and Fleet:: +* Support content packages in UI ({kibana-pull}195831[#195831]). +* Advanced agent monitoring options UI for HTTP endpoint and diagnostics ({kibana-pull}193361[#193361]). +* Adds option to have Kafka dynamic topics in outputs ({kibana-pull}192720[#192720]). +* Adds support for GeoIP processor databases in Ingest Pipelines ({kibana-pull}190830[#190830]). +//// +!!TODO!! The above PR had a lengthy release note description: +The Ingest Pipelines app now supports adding and managing databases for the GeoIP processor. Additionally, the pipeline creation flow now includes support for the IP Location processor. +//// +* Adds agentless ux creation flow ({kibana-pull}189932[#189932]). +* Enable feature flag for reusable integration policies ({kibana-pull}187153[#187153]). +Discover:: +* When writing ES|QL queries, you now get recommendations to help you get started ({kibana-pull}194418[#194418]). +* Enhances the inline documentation experience in ES|QL mode ({kibana-pull}192156[#192156]). +* Adds the ability to break down the histogram by field for ES|QL queries in Discover ({kibana-pull}193820[#193820]). +* Adds a summary column to the Documents table when exploring log data in Discover ({kibana-pull}192567[#192567]). +* Adds row indicators to the Documents table when exploring log data in Discover ({kibana-pull}190676[#190676]). +* Moves the button to switch between ES|QL and classic modes to the toolbar ({kibana-pull}188898[#188898]). +* Adds density settings to allow further customization of the Documents table layout ({kibana-pull}188495[#188495]). +* Enables the time picker for indices without the @timestamp field when editing ES|QL queries ({kibana-pull}184361[#184361]). +Elastic Observability solution:: +* Show monitors from all permitted spaces !! ({kibana-pull}196109[#196109]). +* Adds experimental logs overview to the observability hosts and service overviews ({kibana-pull}195673[#195673]). +* Show alerts for entities ({kibana-pull}195250[#195250]). +* Create sub-feature role to manage APM settings write permissions ({kibana-pull}194419[#194419]). +* Adds related alerts tab to the alert details page ({kibana-pull}193263[#193263]). +* Adds labels field !! ({kibana-pull}193250[#193250]). +* Implement _ignored root cause identification flow ({kibana-pull}192370[#192370]). +* Enable page for synthetics ({kibana-pull}191846[#191846]). +* Settings add config to enable default rules ({kibana-pull}190800[#190800]). +* Added alerts page ({kibana-pull}190751[#190751]). +* Monitor list add bulk delete ({kibana-pull}190674[#190674]). +* Delete monitor API via id param !! ({kibana-pull}190210[#190210]). +* Enable metrics and traces in the Data Set Quality page ({kibana-pull}190043[#190043]). +* Adds alert grouping functionality to the observability alerts page ({kibana-pull}189958[#189958]). +* Adds a new SLO Burn Rate embeddable ({kibana-pull}189429[#189429]). +* The Slack Web API Alert Connector is now supported as a default connector for Synthetics and Uptime rules ({kibana-pull}188437[#188437]). +* Adds option to enable backfill transform ({kibana-pull}188379[#188379]). +* Save the ECS group by fields at the AAD root level ({kibana-pull}188241[#188241]). +* Adds last value aggregation ({kibana-pull}187082[#187082]). +* Improve synthetics alerting ({kibana-pull}186585[#186585]). +* Make overview grid embeddable ({kibana-pull}160597[#160597]). +Elastic Security solution:: +For the Elastic Security 8.16.0 release information, refer to {security-guide}/release-notes.html[_Elastic Security Solution Release Notes_]. +Kibana security:: +* Adds an API endpoint `POST security/roles` that can be used to bulk create or update roles ({kibana-pull}189173[#189173]). +* Automatic Import can now create integrations for logs in the CSV format ({kibana-pull}194386[#194386]). +* Adds an error handling framework to Automatic Import that provides error messages with more context to user ({kibana-pull}193577[#193577]). +* When running in FIPS mode, Kibana forbids usage of PKCS12 configuration options ({kibana-pull}192627[#192627]). +Machine Learning:: +* Adds new section for creating daylight saving time calendar events ({kibana-pull}193605[#193605]). +* Anomaly Detection: Adds a page to list supplied job configurations ({kibana-pull}191564[#191564]). +* Redesigns start/update model deployment dialog to support adaptive resources ({kibana-pull}190243[#190243]). +* File upload: Adds support for PDF files ({kibana-pull}186956[#186956]). +* Adds Pattern analysis embeddable for dashboards ({kibana-pull}186539[#186539]). +Management:: +* This release introduces a fresh, modern look for the console, now featuring the Monaco editor. We've added a file import and export functionality, and the console is fully responsive with stackable panels for a smoother experience. New buttons allow for quick clearing of editor values and output. Additionally, the history and config tabs were improved to enhance usability. ({kibana-pull}189748[#189748]). + +For more information about the features introduced in 8.16.0, refer to <>. + +[[enhancements-and-bug-fixes-v8.16.0]] +=== Enhancements and bug fixes + +For detailed information about the 8.16.0 release, review the enhancements and bug fixes. + + +[float] +[[enhancement-v8.16.0]] +=== Enhancements +Alerting:: +* Allow users to select template while adding a case action in the rule ({kibana-pull}190701[#190701]). +* New full-page rule form in the Stack Management app ({kibana-pull}194655[#194655]). +Dashboards and visualizations:: +* Adds compressed style for dashboard controls ({kibana-pull}190636[#190636]). +* Adds the ability to duplicate a managed dashboard from its `managed` badge ({kibana-pull}189404[#189404]). +* Adds the ability to expand the height of various sections in the Edit ES|QL visualization flyout ({kibana-pull}193453[#193453]). +* Improves the query authoring experience when editing an ES|QL visualization ({kibana-pull}186875[#186875]). +* Syncs the cursor for time series charts powered by ES|QL ({kibana-pull}192837[#192837]). +* Gauge and metric Lens visualizations are no longer experimental ({kibana-pull}192359[#192359]). +* Sets gauge default palette to "temperature" in *Lens* ({kibana-pull}191853[#191853]). +* Supports fuzzy search on field pickers and field lists in *Lens* ({kibana-pull}186894[#186894]). +Data ingestion and Fleet:: +* Update max supported package version ({kibana-pull}196551[#196551]). +* Adds additional columns to Agent Logs UI ({kibana-pull}192262[#192262]). +* Show `+build` versions for Elastic Agent upgrades ({kibana-pull}192171[#192171]). +* Added format parameter to `agent_policies` APIs ({kibana-pull}191811[#191811]). +* Adds toggles for `agent.monitoring.http.enabled` and `agent.monitoring.http.buffer.enabled` to agent policy advanced settings ({kibana-pull}190984[#190984]). +* Support integration policies without agent policy references (aka orphaned integration policies) ({kibana-pull}190649[#190649]). +* Changed the UX of the Edit Integration Policy page to update agent policies ({kibana-pull}190583[#190583]). +* Allow `traces` to be added to the `monitoring_enabled` array in Agent policies ({kibana-pull}189908[#189908]). +* Create task that periodically unenrolls inactive agents ({kibana-pull}189861[#189861]). +* Adds setup technology selector to add integration page ({kibana-pull}189612[#189612]). +* Support integration-level outputs ({kibana-pull}189125[#189125]). +Discover:: +* Renames the Documents tab to Results in ES|QL mode ({kibana-pull}197833[#197833]). +* Adds a cluster details tab for CCS data sources when inspecting requests in ES|QL mode ({kibana-pull}195373[#195373]). +* Adds the query time to the list of statistics when inspecting requests in ES|QL mode ({kibana-pull}194806[#194806]). +* Improves display of error messages in ES|QL mode ({kibana-pull}191320[#191320]). +* Adds a help menu to the ES|QL mode ({kibana-pull}190579[#190579]). +* Initializes the ES|QL editor with time named parameters when switching from the classic mode with a data view without @timestamp ({kibana-pull}189367[#189367]). +* Adds the ability to select multiple rows from the Documents table using "Shift + Select" ({kibana-pull}193619[#193619]). +* Adds the ability to filter on field names and values in the expanded document view ({kibana-pull}192299[#192299]). +* Adds filtering for selected fields ({kibana-pull}191930[#191930]). +* Adds a dedicated column to the document viewer flyout for pinning and unpinning rows ({kibana-pull}190344[#190344]). +* Improves absolute column width handling ({kibana-pull}190288[#190288]). +* Allows filtering by field type in the document viewer flyout ({kibana-pull}189981[#189981]). +* Improves the document viewer flyout to remember the last active tab ({kibana-pull}189806[#189806]). +* Adds ability to hide fields with null values from the document viewer ({kibana-pull}189601[#189601]). +* Adds the ability to copy selected rows as text ({kibana-pull}189512[#189512]). +* Adds a log level badge cell renderer to the Discover logs profile ({kibana-pull}188281[#188281]). +* Shows ECS field descriptions in Discover and adds markdown support for field descriptions ({kibana-pull}187160[#187160]). +* Adds support for the Log overview tab to the Discover log profile ({kibana-pull}186680[#186680]). +* Adds default app state extension and log integration data source profiles ({kibana-pull}186347[#186347]). +* Allows to select and deselect all rows in the grid at once ({kibana-pull}184241[#184241]). +* Limits the height of long field values by default ({kibana-pull}183736[#183736]). +ES|QL editor:: +* Changes the auto-focus to be on the ES|QL editor when loading the page ({kibana-pull}193800[#193800]). +* Updates the autocomplete behavior for `SORT` to be in line with other field-list-based experiences like `KEEP` in ES|QL queries ({kibana-pull}193595[#193595]). +* Adds `all (*)` to the list of suggestions for `COUNT` functions in ES|QL queries ({kibana-pull}192205[#192205]). +* Improves ES|QL autocomplete suggestions for `case()` expressions ({kibana-pull}192135[#192135]). +* Opens suggestions automatically for sources lists and `ENRICH` functions when writing ES|QL queries ({kibana-pull}191312[#191312]). +* Improves wrapping and readability for ES|QL queries ({kibana-pull}191269[#191269]). +* Improves suggestions based on previous function arguments and date suggestions for `bucket` functions in ES|QL queries ({kibana-pull}190828[#190828]). +* Show the `LIMIT` information in the ES|QL editor's footer ({kibana-pull}190498[#190498]). +* Opens suggestions automatically for field lists in ES|QL queries ({kibana-pull}190466[#190466]). +* Integrates a time picker for date fields into the ES|QL editor ({kibana-pull}187047[#187047]). +* Improves ES|QL support for Elasticsearch sub-types in AST for both validation and autocomplete ({kibana-pull}189689[#189689]). +* Adds ECS information to the ES|QL editor suggestions and prioritizes fields based on ECS information on the editor ({kibana-pull}187922[#187922]). +* Improves `BY` suggestions in ES|QL queries to include pipe and comma operators ({kibana-pull}189458[#189458]). +* Makes the suggestion menu open automatically in more places in ES|QL queries ({kibana-pull}189585[#189585]). +* Adds hints upon hover for function argument types and time system types ({kibana-pull}191881[#191881]). +Elastic Observability solution:: +* Enable Kubernetes Otel flow ({kibana-pull}196531[#196531]). +* Pass function responses when copying conversation ({kibana-pull}195635[#195635]). +* Turn 'fast filter' on by default and ensure tech preview badge shows when turned on ({kibana-pull}193710[#193710]). +* Custom Service Name Cell ({kibana-pull}192381[#192381]). +* Remove manage_transform and manage_ingest_pipeline privilege requirements ({kibana-pull}190572[#190572]). +* Create new formula for CPU Usage metric ({kibana-pull}189261[#189261]). +* Adds customizable header for quickstart flows ({kibana-pull}188340[#188340]). +* Change Kubernetes guide to link to observability onboarding ({kibana-pull}188322[#188322]). +* Adds KB user instructions ({kibana-pull}187607[#187607]). +* Refactor Synthetics Overview page for increased scalability ({kibana-pull}187092[#187092]). +* Improve synthetics alerting ({kibana-pull}186585[#186585]). +* Annotations Initial phase ({kibana-pull}184325[#184325]). +Elastic Search solution:: +* Adds Alibaba AI Search to Deletion, search and filtering of inference endpoints ({kibana-pull}190783[#190783]). +Elastic Security solution:: +For the Elastic Security 8.16.0 release information, refer to {security-guide}/release-notes.html[_Elastic Security Solution Release Notes_]. +Kibana security:: +* Enhances Open API spec generation to include Route Security Authorization if available ({kibana-pull}197001[#197001]). +* Automatic Import now analyzes larger number of samples to generate an integration ({kibana-pull}196233[#196233]). +* Extended `KibanaRouteOptions` to include security configuration at the route definition level ({kibana-pull}191973[#191973]). +* Adds several UX improvements to the management of Spaces in **Stack Management > Spaces**, including the ability to assign Roles to an existing Space. ({kibana-pull}191795[#191795]). +* Displays an "invalid file" error when selecting unsupported file types for the user profile image ({kibana-pull}190077[#190077]). +* Displays a warning to users whenever role mappings with empty `any` or `all` rules are created or updated ({kibana-pull}189340[#189340]). +* Adds support for CHIPS cookies ({kibana-pull}188519[#188519]). +* Adds support for Permissions Policy reporting ({kibana-pull}186892[#186892]). +Machine Learning:: +* File upload: enables check for model allocations ({kibana-pull}197395[#197395]). +* Data visualizer: Adds icons for semantic text, sparse vector, and dense vector ({kibana-pull}196069[#196069]). +* Updates vCPUs ranges for start model deployment ({kibana-pull}195617[#195617]). +* Adds ML tasks to the Kibana audit log ({kibana-pull}195120[#195120]). +* Anomaly Detection: adds ability to delete forecasts from job ({kibana-pull}194896[#194896]). +* Updates for Trained Models table layout and model states ({kibana-pull}194614[#194614]). +* Log rate analysis: ensures ability to sort on Log rate change ({kibana-pull}193501[#193501]). +* Single Metric Viewer: Enables cross-filtering for 'by', 'over', and 'partition' field values ({kibana-pull}193255[#193255]). +* Adds link to anomaly detection configurations from Integration > Assets tab ({kibana-pull}193105[#193105]). +* Anomaly Explorer: Displays markers for scheduled events in distribution-type anomaly charts ({kibana-pull}192377[#192377]). +* Serverless Security: Adds ES|QL visualizer menu item to the nav ({kibana-pull}192314[#192314]). +* Updates icons for Machine Learning embeddable dashboard panel types ({kibana-pull}191718[#191718]). +* AIOps: Uses no minimum time range by default for pattern analysis ({kibana-pull}191192[#191192]). +* Links to ML assets from Integration > Assets tab ({kibana-pull}189767[#189767]). +* Utilizes the `DataViewLazy` in ML plugin ({kibana-pull}189188[#189188]). +* AIOps: Chunks groups of field candidates into single queries for top items and histograms ({kibana-pull}189155[#189155]). +* AIOps: Updates fields filter popover to be able to filter fields from analysis (not just grouping) ({kibana-pull}188913[#188913]). +* Single Metric Viewer embeddable: adds forecasting ({kibana-pull}188791[#188791]). +* Adds new custom rule action to force time shift ({kibana-pull}188710[#188710]). +* AIOps: Chunks groups of field candidates into single queries ({kibana-pull}188137[#188137]). +* AIOps: Adds log rate analysis to alert details page contextual insight ({kibana-pull}187690[#187690]). +* Adds ability to toggle visibility for empty fields when choosing an aggregation or field in Anomaly detection, data frame analytics ({kibana-pull}186670[#186670]). +* Anomaly Detection: Adds popover links menu to anomaly explorer charts ({kibana-pull}186587[#186587]). +Management:: +* Adds an option to show or hide empty fields in dropdown lists in Transform ({kibana-pull}195485[#195485]). +* Adds a confirmation dialog when deleting a transform from a warning banner ({kibana-pull}192080[#192080]). +* Improves the autocomplete to suggest fields for the `dense_vector` type in Console ({kibana-pull}190769[#190769]). +* Adds the ability to view an ILM policy details in read-only mode ({kibana-pull}186955[#186955]). + +[float] +[[fixes-v8.16.0]] +=== Bug fixes +Alerting:: +* Show up to 1k maintenance windows in the UI ({kibana-pull}198504[#198504]) +* Skip scheduling actions for the alerts without scheduledActions ({kibana-pull}195948[#195948]). +* Fixes Stack Alerts feature API access control ({kibana-pull}193948[#193948]). +* Remove unintended internal find routes API with public access ({kibana-pull}193757[#193757]). +* Convert timestamp before passing to validation ({kibana-pull}192379[#192379]). +* Grouped over field is not populated correctly when editing a rule ({kibana-pull}192297[#192297]). +* Mark slack rate-limiting errors as user errors ({kibana-pull}192200[#192200]). +* Fixes maintenance window filtering with wildcards ({kibana-pull}194777[#194777]). +* Fixes search filters in rules, alerts, and maintenance windows ({kibana-pull}193623[#193623]). +Cases:: +* Use absolute time ranges when adding visualizations to a case ({kibana-pull}189168[#189168]). +* Fixes custom fields with long text that could not be edited in the UI ({kibana-pull}190490[#190490]). +Dashboards and visualizations:: +* Correctly show full screen mode when opening a dashboard or panel from a URL that contains the fullScreenMode parameter ({kibana-pull}196275[#196275]) and ({kibana-pull}190086[#190086]). +* Fixes an issue that could cause a the dashboard list to stay in loading state ({kibana-pull}195277[#195277]). +* Correctly use the same field icons as Discover ({kibana-pull}194095[#194095]). +* Fixes an issue where panels could disappear from a dashboard when canceling edit after saving the dashboard ({kibana-pull}193914[#193914]). +* Adds scroll margin to panels ({kibana-pull}193430[#193430]). +* Fixes an issue with the breadcrumb update icon not working when clicked ({kibana-pull}192240[#192240]). +* Fixes an issue where unsaved changes could remain after saving a dashboard ({kibana-pull}190165[#190165]). +* Fixes an issue causing the flyout to close when canceling the Save to library action ({kibana-pull}188995[#188995]). +* Fixes incomplete string escaping and encoding in *TSVB* ({kibana-pull}196248[#196248]). +* Fixes an issue where label truncation in heat map legends was not working properly in *Lens* ({kibana-pull}195928[#195928]). +* Fixes an issue where the color picker and axis side settings were incorrectly available in the breakdown dimension editor for XY charts in *Lens* ({kibana-pull}195845[#195845]). +* Fixes the tooltip position on faceted charts in *Vega* ({kibana-pull}194620[#194620]). +* Fixes the filter out legend action for ES|QL visualizations ({kibana-pull}194374[#194374]). +* Fixes element sizing issues in full screen mode in *Vega* ({kibana-pull}194330[#194330]). +* Fixes the default cell text alignment setting for non-numeric field types in *Lens* ({kibana-pull}193886[#193886]). +* Limits the height of the query bar input for long KQL queries ({kibana-pull}193737[#193737]). +* Makes the title correctly align left after removing an icon in **Lens** metric charts ({kibana-pull}191057[#191057]). +* Fixes a "No data" error caused by the "Collapse by" setting in **Lens** metric charts ({kibana-pull}190966[#190966]). +* Fixes an issue causing the color of a cell to disappear when clicking the "Expand cell" icon in *Lens* ({kibana-pull}190618[#190618]). +* Removes unnecessary index pattern references from Lens charts ({kibana-pull}190296[#190296]). +* Fixes several accessibility issues ({kibana-pull}188624[#188624]). +Data ingestion and Fleet:: +* Revert "Fix client-side validation for agent policy timeout fields" ({kibana-pull}194338[#194338]). +* Adds proxy arguments to install snippets ({kibana-pull}193922[#193922]). +* Rollover if dimension mappings changed in dynamic templates ({kibana-pull}192098[#192098]). +Discover:: +* Fixes an issue with search highlighting ({kibana-pull}197607[#197607]). +* Correctly pass embeddable filters to the Surrounding Documents page ({kibana-pull}197190[#197190]). +* Fixes trailing decimals dropped from client side validation messages ({kibana-pull}196570[#196570]). +* Fixes several validation issues and creates an expression type evaluator for ES|QL queries ({kibana-pull}195989[#195989]). +* Fixes duplicate autocomplete suggestions for `WHERE` clauses and suggestions with no space in between in ES|QL queries ({kibana-pull}195771[#195771]). +* Improves variable and field name handling in ES|QL queries ({kibana-pull}195149[#195149]). +* Fixes an issue where the Unified Field List popover could get cut off ({kibana-pull}195147[#195147]). +* Fixes the width for saved object type columns ({kibana-pull}194388[#194388]). +* Adds tooltips to Discover button icons ({kibana-pull}192963[#192963]). +* Excludes inactive integration data stream suggestions ({kibana-pull}192953[#192953]). +* Fixes new variables being suggested in incorrect places ({kibana-pull}192405[#192405]). +* Only log requests in the Inspector when they completed ({kibana-pull}191232[#191232]). +ES|QL editor:: +* Fixes an issue where the autocomplete suggestions could cause duplicate entries in ES|QL queries ({kibana-pull}190465[#190465]). +* Fixes several styling issues in the ES|QL editor ({kibana-pull}190170[#190170]). +Elastic Observability solution:: +* Change the slice outcome from bad to good whenever there is no data during the slice window ({kibana-pull}196942[#196942]). +* Make agent names generic with otel-native mode ({kibana-pull}195594[#195594]). +* Avoid showing unnecessary error toast ({kibana-pull}195331[#195331]). +* Use `fields` instead of `_source` on APM queries ({kibana-pull}195242[#195242]). +* Fixes ping heatmap payload ({kibana-pull}195107[#195107]). +* Fixes rule modal warnings in the developer console ({kibana-pull}194766[#194766]). +* Avoid AI assistant overlaying AI conversations ({kibana-pull}194722[#194722]). +* Improve loading state for metric items ({kibana-pull}192930[#192930]). +* Fixes issue where heatmap UI crashes on undefined histogram data ({kibana-pull}192508[#192508]). +* Calculate the latest metadata lookback based on the calculated history delay ({kibana-pull}191324[#191324]). +* Remove dedicated language setting ({kibana-pull}190983[#190983]). +* Change latest metric to use @timestamp ({kibana-pull}190417[#190417]). +* Prevent initial error when adding filters ({kibana-pull}190214[#190214]). +* Display error message when failing to enable machine learning anomaly detection in Inventory ({kibana-pull}189627[#189627]). +* Convert route validation to Zod ({kibana-pull}188691[#188691]). +* Fixes functions table height in asset details view profiling tab ({kibana-pull}188650[#188650]). +* Adds four decimal places float validation for transaction_sample_rate ({kibana-pull}188555[#188555]). +* Centralize data fetching and better control of when data can be refreshed ({kibana-pull}187736[#187736]). +* Fixes heatmap on monitor detail/history page for very large doc counts ({kibana-pull}184177[#184177]). +* Adds settings to serverless allowlist ({kibana-pull}190098[#190098]). +* Set missing group to false by default and show checkbox value in disable mode ({kibana-pull}188402[#188402]). +Elastic Search solution:: +* Fixes an issue with the {ref}/es-connectors-network-drive.html[Network Drive connector] where advanced configuration fields were not displayed for CSV file role mappings with `Drive Type: Linux` selected. +Elastic Security solution:: +For the Elastic Security 8.16.0 release information, refer to {security-guide}/release-notes.html[_Elastic Security Solution Release Notes_]. +Kibana platform:: +* Fixes an issue causing a wrong date to show in the header of a report when generated from relative date ({kibana-pull}197027[#197027]). +* Fixes an issue where the Created and Updated timestamps for Dashboards were ignoring the default timezone settings in Advanced settings. ({kibana-pull}196977[#196977]). +* Fixes an issue causing searches including a colon `:` character to show inaccurate results ({kibana-pull}190464[#190464]). +Kibana security:: +* Fixes an issue where an LLM was likely to generate invalid processors containing array access in Automatic Import ({kibana-pull}196207[#196207]). +Machine Learning:: +* File upload: fixes PDF character count limit ({kibana-pull}197333[#197333]). +* Data Drift: Updates brush positions on window resize fix ({kibana-pull}196830[#196830]). +* AIOps: Fixes issue where some queries cause filters to not be applied ({kibana-pull}196585[#196585]). +* Transforms: Limits the data grid result window ({kibana-pull}196510[#196510]). +* Fixes Anomaly Swim Lane Embeddable not updating properly on query change ({kibana-pull}195090[#195090]). +* Hides ES|QL based saved searches in ML & Transforms ({kibana-pull}195084[#195084]). +* Fixes query for pattern analysis and change point analysis ({kibana-pull}194742[#194742]). +* Anomaly explorer: Shows data gaps and connect anomalous points on Single Metric Charts ({kibana-pull}194119[#194119]). +* Fixes file upload with no ingest pipeline ({kibana-pull}193744[#193744]). +* Disables field statistics panel in Dashboard if ES|QL is disabled ({kibana-pull}193587[#193587]). +* Fixes display of assignees when attaching ML panels to a new case ({kibana-pull}192163[#192163]). +* Anomaly explorer: Fixes the order of the coordinates displayed on the map tooltip ({kibana-pull}192077[#192077]). +* Fixes links to the Single Metric Viewer from the Annotations and Forecasts tables ({kibana-pull}192000[#192000]). +* Trained models: fixes responsiveness of state column for smaller displays ({kibana-pull}191900[#191900]). +* File upload: increases timeout for upload request ({kibana-pull}191770[#191770]). +* Improves expired license check ({kibana-pull}191503[#191503]). +Management:: +* Fixes the pagination of the source documents data grid in Transforms ({kibana-pull}196119[#196119]). +* Fixes autocomplete suggestions after a comma in Console ({kibana-pull}189656[#189656]). + + [[release-notes-8.15.3]] == {kib} 8.15.3 @@ -3852,7 +4207,7 @@ In 8.1.0 and later, {kib} uses the field caps API, by default, to determine the `visualization:visualize:legacyPieChartsLibrary` has been removed from *Advanced Settings*. The setting allowed you to create aggregation-based pie chart visualizations using the legacy charts library. For more information, refer to {kibana-pull}146990[#146990]. *Impact* + -In 7.14.0 and later, the new aggregation-based pie chart visualization is available by default. For more information, check link:https://www.elastic.co/guide/en/kibana/current/add-aggregation-based-visualization-panels.html[Aggregation-based]. +In 7.14.0 and later, the new aggregation-based pie chart visualization is available by default. For more information, check <>. ==== [discrete] diff --git a/docs/management/action-types.asciidoc b/docs/management/action-types.asciidoc index 361892e430afd..1357af980d278 100644 --- a/docs/management/action-types.asciidoc +++ b/docs/management/action-types.asciidoc @@ -28,9 +28,9 @@ a| <> | Send a request to {gemini}. -a| <> +a| <> -| Send a request to {inference}. +| Send a request to {infer}. a| <> diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index cda03f91dfc17..d6ae2aecaf276 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -330,9 +330,6 @@ the minimum and maximum values of a numeric field or a map of a geo field. [[discover:showMultiFields]]`discover:showMultiFields`:: Controls the display of multi-fields in the expanded document view. -[[discover:showLegacyFieldTopValues]]`discover:showLegacyFieldTopValues`:: -To calculate the top values for a field in the sidebar using 500 instead of 5,000 records per shard, turn on this option. - [[discover-sort-defaultorder]]`discover:sort:defaultOrder`:: The default sort direction for time-based data views. diff --git a/docs/management/connectors/action-types/inference.asciidoc b/docs/management/connectors/action-types/inference.asciidoc index 8c7f2840f9c5c..ea8a0be675e18 100644 --- a/docs/management/connectors/action-types/inference.asciidoc +++ b/docs/management/connectors/action-types/inference.asciidoc @@ -1,7 +1,7 @@ [[inference-action-type]] == {infer-cap} connector and action ++++ -{inference} +{infer-cap} ++++ :frontmatter-description: Add a connector that can send requests to {inference}. :frontmatter-tags-products: [kibana] @@ -9,7 +9,8 @@ :frontmatter-tags-user-goals: [configure] -The {infer} connector uses the {es} client to send requests to an {infer} service. The connector uses the <> to send the request. +The {infer} connector uses the {es} client to send requests to an {infer} service. +The connector uses the <> to send the request. [float] [[define-inference-ui]] @@ -19,7 +20,7 @@ You can create connectors in *{stack-manage-app} > {connectors-ui}*. For example [role="screenshot"] image::management/connectors/images/inference-connector.png[{inference} connector] -// NOTE: This is an autogenerated screenshot. Do not edit it directly. + [float] [[inference-connector-configuration]] @@ -44,7 +45,8 @@ while creating or editing the connector in {kib}. For example: [role="screenshot"] image::management/connectors/images/inference-completion-params.png[{infer} params test] -// NOTE: This is an autogenerated screenshot. Do not edit it directly. + + [float] [[inference-connector-actions]] === {infer-cap} connector actions @@ -56,14 +58,17 @@ The {infer} actions have the following configuration properties. Properties depe ==== Completion The following example performs a completion task on the example question. + Input:: The text on which you want to perform the {infer} task. For example: + -[source,text] -- +[source,text] +------------------------------------------------------------ { input: 'What is Elastic?' } +------------------------------------------------------------ -- [float] @@ -71,18 +76,22 @@ The text on which you want to perform the {infer} task. For example: ==== Text embedding The following example performs a text embedding task. + Input:: The text on which you want to perform the {infer} task. For example: + -[source,text] -- +[source,text] +------------------------------------------------------------ { input: 'The sky above the port was the color of television tuned to a dead channel.', task_settings: { input_type: 'ingest' } } +------------------------------------------------------------ -- + Input type:: An optional string that overwrites the connector's default model. @@ -91,16 +100,20 @@ An optional string that overwrites the connector's default model. ==== Reranking The following example performs a reranking task on the example input. + Input:: The text on which you want to perform the {infer} task. Should be a string array. For example: + -[source,text] -- +[source,text] +------------------------------------------------------------ { input: ['luke', 'like', 'leia', 'chewy', 'r2d2', 'star', 'wars'], query: 'star wars main character' } +------------------------------------------------------------ -- + Query:: The search query text. @@ -109,14 +122,17 @@ The search query text. ==== Sparse embedding The following example performs a sparse embedding task on the example sentence. + Input:: The text on which you want to perform the {infer} task. For example: + -[source,text] -- +[source,text] +------------------------------------------------------------ { input: 'The sky above the port was the color of television tuned to a dead channel.' } +------------------------------------------------------------ -- [float] diff --git a/docs/settings/alert-action-settings.asciidoc b/docs/settings/alert-action-settings.asciidoc index db36248ef194f..e0fa3f0aab860 100644 --- a/docs/settings/alert-action-settings.asciidoc +++ b/docs/settings/alert-action-settings.asciidoc @@ -112,11 +112,6 @@ A boolean value indicating that TLS must be used for this connection. The options `smtp.ignoreTLS` and `smtp.requireTLS` can not both be set to true. Default: `false`. -`xpack.actions.customHostSettings[n].ssl.rejectUnauthorized`:: -deprecated:[8.0.0] Use <> instead. A boolean value indicating whether to bypass server certificate validation. -Overrides the general `xpack.actions.rejectUnauthorized` configuration -for requests made for this hostname/port. - [[action-config-custom-host-verification-mode]] `xpack.actions.customHostSettings[n].ssl.verificationMode` {ess-icon}:: Controls the verification of the server certificate that {kib} receives when making an outbound SSL/TLS connection to the host server. Valid values are `full`, `certificate`, and `none`. Use `full` to perform hostname verification, `certificate` to skip hostname verification, and `none` to skip verification. Default: `full`. <>. Overrides the general `xpack.actions.ssl.verificationMode` configuration @@ -198,19 +193,10 @@ By default, no hosts will use the proxy, but if an action's hostname is in this `xpack.actions.proxyHeaders` {ess-icon}:: Specifies HTTP headers for the proxy, if using a proxy for actions. Default: {}. -`xpack.actions.proxyRejectUnauthorizedCertificates` {ess-icon}:: -deprecated:[8.0.0] Use <> instead. Set to `false` to bypass certificate validation for the proxy, if using a proxy for actions. Default: `true`. - [[action-config-proxy-verification-mode]]`xpack.actions.ssl.proxyVerificationMode` {ess-icon}:: Controls the verification for the proxy server certificate that Kibana receives when making an outbound SSL/TLS connection to the proxy server. Valid values are `full`, `certificate`, and `none`. Use `full` to perform hostname verification, `certificate` to skip hostname verification, and `none` to skip verification. Default: `full`. <>. -`xpack.actions.rejectUnauthorized` {ess-icon}:: -deprecated:[8.0.0] Use <> instead. Set to `false` to bypass certificate validation for actions. Default: `true`. -+ -As an alternative to setting `xpack.actions.rejectUnauthorized`, you can use the setting -`xpack.actions.customHostSettings` to set SSL options for specific servers. - [[action-config-verification-mode]] `xpack.actions.ssl.verificationMode` {ess-icon}:: Controls the verification for the server certificate that {hosted-ems} receives when making an outbound SSL/TLS connection for actions. Valid values are `full`, `certificate`, and `none`. Use `full` to perform hostname verification, `certificate` to skip hostname verification, and `none` to skip verification. Default: `full`. <>. diff --git a/docs/upgrade-notes.asciidoc b/docs/upgrade-notes.asciidoc index 5bde93df15490..98f7feeac2d6a 100644 --- a/docs/upgrade-notes.asciidoc +++ b/docs/upgrade-notes.asciidoc @@ -1019,7 +1019,7 @@ In 8.1.0 and later, {kib} uses the field caps API, by default, to determine the `visualization:visualize:legacyPieChartsLibrary` has been removed from *Advanced Settings*. The setting allowed you to create aggregation-based pie chart visualizations using the legacy charts library. For more information, refer to {kibana-pull}146990[#146990]. *Impact* + -In 7.14.0 and later, the new aggregation-based pie chart visualization is available by default. For more information, check link:https://www.elastic.co/guide/en/kibana/current/add-aggregation-based-visualization-panels.html[Aggregation-based]. +In 7.14.0 and later, the new aggregation-based pie chart visualization is available by default. For more information, check <>. ==== [discrete] @@ -1694,6 +1694,30 @@ When you create *Lens* visualization, the default for the *Legend width* is now [float] ==== Elastic Observability solution +[discrete] +[[deprecation-192003]] +* Deprecated the Observability AI Assistant specific advanced setting `observability:aiAssistantLogsIndexPattern`. (8.16) +[%collapsible] +==== +*Details* + +The Observability AI Assistant specific advanced setting for Logs index patterns `observability:aiAssistantLogsIndexPattern` is deprecated and no longer used. The AI Assistant will now use the existing **Log sources** setting `observability:logSources` instead. For more information, refer to ({kibana-pull}192003[#192003]). + +//*Impact* + +//!!TODO!! +==== + +[discrete] +[[deprecation-194519]] +* The Logs Stream was hidden by default in favor of the Logs Explorer app. (8.16) +[%collapsible] +==== +*Details* + +You can find the Logs Explorer app in the navigation menu under Logs > Explorer, or as a separate tab in Discover. For more information, refer to ({kibana-pull}194519[#194519]). + +*Impact* + +You can still show the Logs Stream app again by navigating to Stack Management > Advanced Settings and by enabling the `observability:enableLogsStream` setting. +==== + [discrete] [[deprecation-120689]] diff --git a/docs/user/dashboard/create-dashboards.asciidoc b/docs/user/dashboard/create-dashboards.asciidoc index b07b4e88a684a..8b0d5e5f524fd 100644 --- a/docs/user/dashboard/create-dashboards.asciidoc +++ b/docs/user/dashboard/create-dashboards.asciidoc @@ -83,6 +83,7 @@ image::https://images.contentstack.io/v3/assets/bltefdd0b53724fa2ce/bltf75cdb828 [[save-dashboards]] . **Save** the dashboard. You can then leave the **Edit** mode and *Switch to view mode*. +NOTE: Managed dashboards can't be edited directly, but you can <> them and edit these duplicates. [[reset-the-dashboard]] === Reset dashboard changes @@ -155,6 +156,24 @@ Copy panels from one dashboard to another dashboard. [role="screenshot"] image:https://images.contentstack.io/v3/assets/bltefdd0b53724fa2ce/blt64206db263cf5514/66f49286833cffb09bebd18d/copy-to-dashboard-8.15.0.gif[Copy a panel to another dashboard, width 30%] +[[duplicate-dashboards]] +== Duplicate dashboards + +. Open the dashboard you want to duplicate. + +. In *View* mode, click *Duplicate* in the toolbar. + +. In the *Duplicate dashboard* window, enter a title and optional description and tags. + +. Click *Save*. + +You will be redirected to the duplicated dashboard. + +To duplicate a managed dashboard, follow the instructions above or click the *Managed* badge in the toolbar. Then click *Duplicate* in the dialogue that appears. + +[role="screenshot"] +image::images/managed-dashboard-popover-8.16.0.png[Managed badge dialog with Duplicate button, width=40%] + == Import dashboards You can import dashboards from the **Saved Objects** page under **Stack Management**. Refer to <>. diff --git a/docs/user/dashboard/dashboard.asciidoc b/docs/user/dashboard/dashboard.asciidoc index 5ca198c9831af..2bc6738516f15 100644 --- a/docs/user/dashboard/dashboard.asciidoc +++ b/docs/user/dashboard/dashboard.asciidoc @@ -17,7 +17,7 @@ There are several <> in {kib} that let you create // add link to sharing section At any time, you can <> you've created with your team, in {kib} or outside. -Some dashboards are created and managed by the system, and are identified as `managed` in your list of of dashboards. This generally happens when you set up an integration to add data. You can't edit managed dashboards directly, but you can duplicate them and edit these duplicates. +Some dashboards are created and managed by the system, and are identified as `managed` in your list of dashboards. This generally happens when you set up an integration to add data. You can't edit managed dashboards directly, but you can <> them and edit these duplicates. -- diff --git a/docs/user/dashboard/drilldowns.asciidoc b/docs/user/dashboard/drilldowns.asciidoc index 6b3a6d80ecdda..cb568d97e69ee 100644 --- a/docs/user/dashboard/drilldowns.asciidoc +++ b/docs/user/dashboard/drilldowns.asciidoc @@ -1,6 +1,6 @@ [role="xpack"] [[drilldowns]] -=== Drilldowns +=== Add drilldowns Panels have built-in interactive capabilities that apply filters to the dashboard data. For example, when you drag a time range or click a pie slice, a filter for the time range or pie slice is applied. Drilldowns let you customize the interactive behavior while keeping the context of the interaction. diff --git a/docs/user/dashboard/images/managed-dashboard-popover-8.16.0.png b/docs/user/dashboard/images/managed-dashboard-popover-8.16.0.png new file mode 100644 index 0000000000000..b1cd0562a42c7 Binary files /dev/null and b/docs/user/dashboard/images/managed-dashboard-popover-8.16.0.png differ diff --git a/docs/user/images/dashboard-star.png b/docs/user/images/dashboard-star.png new file mode 100644 index 0000000000000..25219d8866c0b Binary files /dev/null and b/docs/user/images/dashboard-star.png differ diff --git a/docs/user/images/dashboard-usage.png b/docs/user/images/dashboard-usage.png new file mode 100644 index 0000000000000..e18843511e21a Binary files /dev/null and b/docs/user/images/dashboard-usage.png differ diff --git a/docs/user/images/discover-log-level.png b/docs/user/images/discover-log-level.png new file mode 100644 index 0000000000000..a6de92c0ae020 Binary files /dev/null and b/docs/user/images/discover-log-level.png differ diff --git a/docs/user/images/esql-autocomplete-suggestions.png b/docs/user/images/esql-autocomplete-suggestions.png new file mode 100644 index 0000000000000..bd78201b0d121 Binary files /dev/null and b/docs/user/images/esql-autocomplete-suggestions.png differ diff --git a/docs/user/images/esql-suggestions.png b/docs/user/images/esql-suggestions.png new file mode 100644 index 0000000000000..234f0339003a1 Binary files /dev/null and b/docs/user/images/esql-suggestions.png differ diff --git a/docs/user/images/ip-location-processor.png b/docs/user/images/ip-location-processor.png new file mode 100644 index 0000000000000..b1de4a540f52d Binary files /dev/null and b/docs/user/images/ip-location-processor.png differ diff --git a/docs/user/images/metric-customization.png b/docs/user/images/metric-customization.png new file mode 100644 index 0000000000000..238df1aee82ac Binary files /dev/null and b/docs/user/images/metric-customization.png differ diff --git a/docs/user/images/monaco-console.png b/docs/user/images/monaco-console.png new file mode 100644 index 0000000000000..3bdd4be4eb498 Binary files /dev/null and b/docs/user/images/monaco-console.png differ diff --git a/docs/user/images/solution-view-obs.png b/docs/user/images/solution-view-obs.png new file mode 100644 index 0000000000000..4ae5942dbae37 Binary files /dev/null and b/docs/user/images/solution-view-obs.png differ diff --git a/docs/user/images/space-settings.png b/docs/user/images/space-settings.png new file mode 100644 index 0000000000000..a3a38c1ca88c7 Binary files /dev/null and b/docs/user/images/space-settings.png differ diff --git a/docs/user/images/table-coloring.png b/docs/user/images/table-coloring.png new file mode 100644 index 0000000000000..6c96daf381164 Binary files /dev/null and b/docs/user/images/table-coloring.png differ diff --git a/docs/user/ml/index.asciidoc b/docs/user/ml/index.asciidoc index e84ca23dbc84d..91227055fa8a7 100644 --- a/docs/user/ml/index.asciidoc +++ b/docs/user/ml/index.asciidoc @@ -51,7 +51,8 @@ information, refer to {ml-docs}/ml-limitations.html[{ml-cap}]. preview::[] -You can find the data drift view in **{ml-app}** > *{data-viz}* in {kib}. +You can find the data drift view in **{ml-app}** > *{data-viz}* in {kib} or by using +the <>. The data drift view shows you the differences in each field for two different time ranges in a given {data-source}. The view helps you to visualize the changes in your data over time and enables you to understand its behavior @@ -167,7 +168,7 @@ It makes it easy to find and investigate causes of unusual spikes or drops by us Examine the histogram chart of the log rates for a given {data-source}, and find the reason behind a particular change possibly in millions of log events across multiple fields and values. You can find log rate analysis embedded in multiple applications. -In {kib}, you can find it under **{ml-app}** > **AIOps Labs** where you can select the {data-source} or saved search that you want to analyze. +In {kib}, you can find it under **{ml-app}** > **AIOps Labs** or by using the <>. Here, you can select the {data-source} or saved search that you want to analyze. [role="screenshot"] image::user/ml/images/ml-log-rate-analysis-before.png[Log event histogram chart] @@ -201,8 +202,8 @@ displays them together with a chart that shows the distribution of each category and an example document that matches the category. //end::log-pattern-analysis-intro[] -You can find log pattern analysis under **{ml-app}** > **AIOps Labs** where you -can select the {data-source} or saved search that you want to analyze, or in +You can find log pattern analysis under **{ml-app}** > **AIOps Labs** or by using the <>. +Here, you can select the {data-source} or saved search that you want to analyze, or in **Discover** as an available action for any text field. [role="screenshot"] @@ -226,8 +227,8 @@ Change point detection uses the to detect distribution changes, trend changes, and other statistically significant change points in a metric of your time series data. -You can find change point detection under **{ml-app}** > **AIOps Labs** where -you can select the {data-source} or saved search that you want to analyze. +You can find change point detection under **{ml-app}** > **AIOps Labs** or by using the <>. +Here, you can select the {data-source} or saved search that you want to analyze. [role="screenshot"] image::user/ml/images/ml-change-point-detection.png[Change point detection UI] diff --git a/docs/user/whats-new.asciidoc b/docs/user/whats-new.asciidoc index 2a726ba3dc4f3..25568518ad2ec 100644 --- a/docs/user/whats-new.asciidoc +++ b/docs/user/whats-new.asciidoc @@ -1,175 +1,144 @@ [[whats-new]] -== What's new in 8.15 +== What's new in 8.16 -Here are the highlights of what's new and improved in 8.15. +Here are the highlights of what's new and improved in 8.16. For detailed information about this release, check the <>. -Previous versions: {kibana-ref-all}/8.14/whats-new.html[8.14] | {kibana-ref-all}/8.13/whats-new.html[8.13] | {kibana-ref-all}/8.12/whats-new.html[8.12] | {kibana-ref-all}/8.11/whats-new.html[8.11] | {kibana-ref-all}/8.10/whats-new.html[8.10] | {kibana-ref-all}/8.9/whats-new.html[8.9] | {kibana-ref-all}/8.8/whats-new.html[8.8] | {kibana-ref-all}/8.7/whats-new.html[8.7] | {kibana-ref-all}/8.6/whats-new.html[8.6] | {kibana-ref-all}/8.5/whats-new.html[8.5] | {kibana-ref-all}/8.4/whats-new.html[8.4] | {kibana-ref-all}/8.3/whats-new.html[8.3] | {kibana-ref-all}/8.2/whats-new.html[8.2] | {kibana-ref-all}/8.1/whats-new.html[8.1] | {kibana-ref-all}/8.0/whats-new.html[8.0] +Previous versions: {kibana-ref-all}/8.15/whats-new.html[8.15] | {kibana-ref-all}/8.14/whats-new.html[8.14] | {kibana-ref-all}/8.13/whats-new.html[8.13] | {kibana-ref-all}/8.12/whats-new.html[8.12] | {kibana-ref-all}/8.11/whats-new.html[8.11] | {kibana-ref-all}/8.10/whats-new.html[8.10] | {kibana-ref-all}/8.9/whats-new.html[8.9] | {kibana-ref-all}/8.8/whats-new.html[8.8] | {kibana-ref-all}/8.7/whats-new.html[8.7] | {kibana-ref-all}/8.6/whats-new.html[8.6] | {kibana-ref-all}/8.5/whats-new.html[8.5] | {kibana-ref-all}/8.4/whats-new.html[8.4] | {kibana-ref-all}/8.3/whats-new.html[8.3] | {kibana-ref-all}/8.2/whats-new.html[8.2] | {kibana-ref-all}/8.1/whats-new.html[8.1] | {kibana-ref-all}/8.0/whats-new.html[8.0] [discrete] -=== ES|QL +=== Solution-oriented navigation +On Elastic Cloud Hosted deployments running on version 8.16, you can now navigate Kibana using a lighter, solution-oriented left navigation menu, called **Solution view**. -[discrete] -==== Filter UX improvements in ES|QL +There are four selectable solution views: Search, Observability, Security, and Classic. Search, Observability, and Security are the new navigation menus. Each of those brings simplicity by focusing the left navigation menu on a relevant subset of features, scoped to its associated use cases, and offers a dedicated home page. Classic has the same navigation menu as 8.15 and before. -We're thrilled to unveil a complete overhaul of filtering in the ES|QL UX. Now, you can seamlessly filter data by browsing a time series chart, allowing for quick and intuitive time-based filtering. Interactive chart filtering lets you refine your data directly by clicking on any chart, while creating WHERE clause filters from the Discover table or sidebar has never been easier. These enhancements streamline data exploration and analysis, making your ES|QL experience more efficient and user-friendly than ever. +Each space has its own solution view setting which determines the navigation experience for all users of that space. -*Filter by clicking a chart:* +When creating a new deployment, you will now be asked to choose between one of the 3 new solution views for your default space. If you prefer to stick with the classic, multi-layered navigation, you can do so once the deployment is created by navigating to your space settings. -image::https://images.contentstack.io/v3/assets/bltefdd0b53724fa2ce/blt965a5190f246f7c8/669a7d41e5f7c84793b031cb/filter-by-clicking-chart.gif[Filter by clicking a chart] +Deployments upgrading from a previous version to 8.16 keep the classic navigation. Admins can enable one of the new solution views from the space settings. -*Filter by browsing a time series chart:* +image::images/solution-view-obs.png[Example of observability solution view] +_The Observability solution view and its Home page._ -image::https://images.contentstack.io/v3/assets/bltefdd0b53724fa2ce/blta20c9a93dded707c/669a7d40843f93a02fe51013/filter-by-brushing-time-series.gif[Filter by browsing a time series chart] +[discrete] +=== Discover and ES|QL -*Create WHERE clause filters from Discover table or sidebar:* +[discrete] +==== Contextual Data presentation -image::https://images.contentstack.io/v3/assets/bltefdd0b53724fa2ce/blt50ac35ab3af29ff8/669a7d4006a6fafe4c7cb39d/create-where-clause-filters-from-sidebar.gif[Create WHERE clause filters from Discover table or sidebar] +In this release, Discover introduces enhanced contextual data presentation. Previously, you needed to manually select relevant fields and set up your workspace before diving into data exploration. Now, Discover automatically tailors the user experience based on the data being explored, powered by a scalable contextual architecture. For example, when analyzing logs, you'll see a *log.level* field rendered directly in the table, a custom Logs overview in the document viewer, and log.level indicators on individual rows. +image::images/discover-log-level.png[Log level badge displaying in the Discover grid] [discrete] -==== Field statistics in ES|QL +==== Recommended ES|QL queries -Field statistics are now available in ES|QL. This feature is designed to provide comprehensive insights for each data field. With this enhancement, you can access detailed statistics such as distributions, averages, and other key metrics, helping you quickly understand your data. This makes data exploration and quality assessment more efficient, providing deeper insights and streamlining the analysis of field-level data in ES|QL. +Writing ES|QL queries just got easier. Many users face challenges when authoring queries, and even more so when unfamiliar with the syntax or data structure. This can lead to inefficiencies in data analysis and visualization. We want to reduce the time it takes to create queries and to lower the learning curve for both new and existing users by suggesting recommended queries within the ES|QL Help menu and from the auto-complete. -image::images/field-statistics-esql.png[Field statistics in ES|QL] +image::images/esql-suggestions.png[A list of suggestions to get started with an ES|QL query, width=30%] +_Recommended ES|QL queries from the ES|QL help menu_ -[discrete] -==== Integrations support in the ES|QL editor when using FROM command. +image::images/esql-autocomplete-suggestions.png[A list of suggestions in the autocomplete menu of an ES|QL query, width=50%] +_Recommended ES|QL queries from auto-complete suggestions_ -We're excited to announce enhanced support for integrations in the ES|QL editor with the *FROM* command. Previously, you could only access indices, but now you can also view a list of installed integrations directly within the editor. This improvement streamlines your workflow, making it easier to manage and utilize various integrations while working with your data. - -image::images/integrations-in-esql.png[Accessing an integration from ES|QL] [discrete] === Dashboards [discrete] -==== Field statistics in Dashboards - -It's now easier than ever to include your field statistics view from **Discover** into **Dashboards**. While running investigations, it is very common that you need to see some field information, such as unique values and their distribution, to make sense of the data. Select the fields that you want with your ES|QL query and get the document count, values, and distribution in your dashboard so you don't have to navigate back and forth to **Discover** to see this information. +==== Manage dashboards more easily and efficiently +As part of a series of improvements to help you find and manage your dashboards https://www.elastic.co/guide/en/kibana/8.15/whats-new.html#_view_dashboard_creator_and_last_editor[started in version 8.15], the new default way to sort your dashboards is by recently viewed, and we are adding an option to star your favorite dashboards, as well as some statistics to monitor the usage of your dashboards. -image::https://images.contentstack.io/v3/assets/bltefdd0b53724fa2ce/blt9bc52ff7851acc52/669a4f6a490fbc64fa22f279/field-statistics.gif[Showing field statistics panel in Dashboards] +You can find your favorite dashboards in the new **Starred** tab. -[discrete] -==== Statistics in legends +image::images/dashboard-star.png[Viewing starred dashboards] -Accelerate time to insights by summarizing the values of your charts using average, minimum, maximum, median, and variance, among many others. You can add these statistics for **Lens** and ES|QL visualizations. It is important to note that these statistics are computed using the data points from the chart considering the aggregation used and not the raw data. In the following example, the chart shows the median memory per host, so the Max = 15.3KB for the first series (artifacts.elastic.co) is the maximum value of the median memory per host. +By opening a dashboard's details using the “info” icon from the dashboard list view, you can now get a sense of the popularity of that dashboard with a histogram showing how many times the dashboard was viewed in the last 90 days. -image::images/statistics-in-legends.png[Statistics in legends] +image::images/dashboard-usage.png[Dashboard usage chart] -You can find the option to select statistics for your legends along with an explanation for each calculation when editing your visualization, as shown in the following image. +[discrete] +==== Log Pattern Analysis dashboard panels +Log Pattern Analysis panels are now available for you to add to your dashboards, making AIOps even more embedded in your workflows and where you need it. When filtering patterns, the dashboard’s data adjusts accordingly. You can also choose the filtering to transition you into Discover for further exploration. -image::images/statistics-in-legends2.png[Select statistics in legends] +image:https://images.contentstack.io/v3/assets/bltefdd0b53724fa2ce/blt8288e01386b5830c/67222fb0d2da223e27bc1e67/log_analysis_panel.gif[Log pattern analysis panel in dashboards] [discrete] -==== View dashboard creator and last editor +==== Color text values in tables +Previously, you could only decide to color numeric values in tables. We're adding the ability to also color your string values. You can decide whether you want to color the whole cell, or only the text. -You can now see who created and who last updated a dashboard. +image::images/table-coloring.png[Coloring table cells with string values] -You can find the creator information right from the dashboard list. -image::images/dashboard-creator.png[Dashboard creator column in dashboard list] +[discrete] +==== Formatting options for your metrics +We've received a lot of feedback asking for more flexibility to customize the appearance of your metrics. In this version, we are adding the ability to customize the title and value alignment, as well as the font size. Selecting the *Fit* option will adjust the font size and make the metric value occupy the entire panel. -Quickly find all dashboards created by the same user with a simple filter. +image::images/metric-customization.png[Customization options for a metric panel] -image::images/dashboard-creator-filter.png[Filtering dashboards by creator] -Note that the creator information will be visible only for dashboards created on or after version 8.14. -You can also see who last updated a dashboard by clicking the dashboard information icon from the dashboard list. The creator is also visible next to it. This information is immutable and cannot be changed. +//[discrete] +//=== Alerting, cases, and connectors -image::images/dashboard-last-editor.png[Dashboard details panel with the name of the last editor] [discrete] -=== Discover +=== Managing {kib} and data [discrete] -==== Push flyout for Discover document viewer +==== Edit space access from the space settings +As an admin, you can now assign roles to and edit role permissions on a given space directly from the settings of that space. -You can now seamlessly view document details and the main table simultaneously in **Discover** with the new _push_ flyout. You can adjust the width of the flyout to suit your needs and explore your data much more easily. - -image::https://images.contentstack.io/v3/assets/bltefdd0b53724fa2ce/bltb40a408acf4ab688/669a58ea9fecd85219d58ed2/discover-push-flyout.gif[Resizable push flyout in Discover] +Prior to 8.16, you could only do this from the role settings, which was counterintuitive. +image::space-settings.png[Editing space settings with new options] [discrete] -=== Alerting, cases, and connectors +==== New IP Location processor +Enhancing location information based on IP addresses just got easier with the new IP Location processor. In addition to the existing free GeoLite offerings from MaxMind, we have integrated with MaxMind’s premium GeoIP databases for users who have licensed MaxMind’s products. If you're an Enterprise Elastic customer, you now have an additional third-party product, IP Info, available for use as well. These additional data sources provide improved options for enriching data with location information associated with IP addresses to improve telemetry and insights. To utilize these features beyond the free MaxMind GeoIP database, you will need to have licensed premium MaxMind products and/or the IP Info database. -[discrete] -==== Case templates - -{kib} cases offer a new powerful capability to enhance the efficiency of your analyst teams with <>. -You can manage multiple templates, each of which can be used to auto-populate values in a case with pre-defined knowledge. -This streamlines the investigative process and significantly reduces time to resolution. +image::images/ip-location-processor.png[The IP Location processor] [discrete] -==== Case custom fields are GA +==== File uploader PDF support +The file uploader provides a quick way to upload data and start using Elastic. In 8.16, we are improving it to allow you to upload data from PDF files. -In 8.11, <> were added to cases and they are now moving from technical preview to general availability. -You can set custom field values in your templates to enhance consistency across cases. +image:https://images.contentstack.io/v3/assets/bltefdd0b53724fa2ce/blte8f0b295330b7e68/67222fb0ca492a5044b51bd8/file_uploader_pdf.gif[File uploader with PDF support] [discrete] -==== {sn} additional fields +=== Developer Tools Console redesign +We're excited to introduce a number of improvements to the overall user experience on one of our most popular features: **Console**. If you're new to Console, you will be welcomed by an onboarding tour that will help you get started quickly with your first requests. And if you're already a regular Console user, you will notice a variety of new features, including the ability to copy outputs to the clipboard, import and export request files, enjoy improved responsiveness, and other quality of life improvements. -You can now create enriched {sn} tickets based on detected alerts with a more comprehensive structure that matches the {sn} ticket scheme. -A new JSON field is now available as part of the {sn} action, which enables you to send any field from {kib} alerts to {sn} tickets. - -[discrete] -==== {webhook-cm} SSL auth support - -It's common for organizations to integrate with third parties using secured authentication. -Currently, most of the available case connectors use basic authentication (user and passwords or tokens), which might not be sufficient to meet organization security policies. -With this release, the <> now supports client certification, which enables you to leverage the connector for secured integration with third parties. - -The {webhook-cm} connector also moves from technical preview to general availability in this release. +image::images/monaco-console.png[Console's redesign featuring the Monaco editor] [discrete] === Machine Learning [discrete] -==== Improved UX for Log Pattern Analysis in Discover +==== The Inference API is now Generally Available -Analyze large volumes of logs efficiently, in very short times with Log Pattern Analysis in **Discover**. In 8.15, we redesigned the Log Pattern Analysis user flow in **Discover** to make it easier to use. Discover log patterns with one click for the message field (and other applicable text fields) and easily filter in and out logs to drastically reduce MTTR. - -image::https://images.contentstack.io/v3/assets/bltefdd0b53724fa2ce/blt7e63d7e764ab183e/669a807bd316c7015db35458/ml-log-pattern-analysis.gif[New log pattern analysis interface] +Starting in 8.16, the {ref}/inference-apis.html[Inference API] is now GA, offering production-level stability, robustness and performance. Elastic’s Inference API integrates the state-of-the-art in AI inference, including ELSER, your Elastic hosted models and {ref}/put-inference-api.html#put-inference-api-desc[an increasing array of external models and tasks] in a unified, lean syntax. Used with {ref}/semantic-text.html[semantic_text] or the vector fields supported by the Elastic vector database, you can perform AI search, reranking, and completion with simplicity. In 8.16, we're also adding streamed completions for improved flows and real time interactions and GenAI experiences. [discrete] -==== Log Rate Analysis contextual insights in serverless Observability +==== ELSER and trained models adaptive resources and chunking strategies -You can now see insights in natural language, for example for the root cause of a log rate change or threshold alert, in Log Rate Analysis. This feature is currently only available for Observability serverless projects. +From 8.16, ELSER and the other AI search and NLP models you use in Elastic automatically adapt resource consumption according to the inference load, providing the performance you need during peak times and reducing the cost during slow periods, all the way down to zero cost during idle times. -image::images/obs-log-rate-analysis-insigths.png[Log Rate Analysis contextual insights in serverless Observability] +We're also improving the UX through which you deploy your models. You can provision search-optimized and ingest-optimized model deployments with a one-click selection. An optimized configuration is created without the need to specify parameters such as threads and allocations. Combined with the flexibility of ML auto-scaling on Elastic Cloud and the incredible elasticity of Elastic Cloud Serverless, you are in full control of both performance and cost. -[discrete] -==== Inference API improvements +image::https://images.contentstack.io/v3/assets/bltefdd0b53724fa2ce/blt429790e1de1b4f93/67222fb048ec8c73255ef4eb/trained_models.gif[Trained models and ELSER] -The inference API provides a seamless, intuitive interface to perform inference and other tasks against proprietary, hosted, and integrated external services. In 8.15, we're extending it with the following capabilities: +In addition, from 8.16 you can choose between a word or sequence-based chunking strategy to use with your trained models, and you can also customize the maximum size and overlap parameters. A suitable chunking strategy can result in gains depending on the model you use, the length and nature of the texts and the length and complexity of the search queries. -* Support for Anthropic's chat completion API. -* Ability to host cross encoder models and perform the reranking task. - - -[discrete] -=== Managing {kib} users and objects [discrete] -==== Sharing improvements +==== Support for Daylight Saving Time changes in Anomaly Detection -You can now share a dashboard, search, or Lens object in one click. When sharing an object, the most common actions are directly presented to you, and a short link is automatically generated, making it simpler than ever to share your work. +In 8.16, we are introducing support for DST changes in Anomaly Detection. Set up a DST calendar by selecting the right timezone and apply it to your anomaly detection jobs individually or in groups. This feature eliminates any false positives that you may have experienced previously due to Daylight Saving Time changes, and works without the need for your intervention for many years ahead. -image::images/share-modal.png[New object share modal, width=50%] - -[discrete] -==== Quick API key creation - -Many API keys don’t require custom settings, so we made it simple to generate a standard key. From the **Endpoints & API keys** top menu in Search, you can create a key in seconds. - -image::images/create-simple-api-key.png[Shortcut to create an API key, width=60%] - -[discrete] -==== Filtering by User in Kibana Audit Logs +image::https://images.contentstack.io/v3/assets/bltefdd0b53724fa2ce/blt5fb82f18cde26710/67222fb086339971144a31e5/daylight_savings.gif[DST support in Anomaly Detection] -We are pleased to share that ignoring events by user in Kibana audit logs is now possible. This enhancement will give you more flexibility to reduce the overall number of events logged by the Kibana audit logs service and to control the volume of data being generated in audit logs. While we currently offer a number of ways to do this using the `xpack.security.audit.ignore_filters.[]` configuration setting, there wasn't an easy option to filter by user. With this addition, you can configure Kibana audit logs to ignore events based on values from the following fields: users, spaces, outcomes, categories, types and actions. \ No newline at end of file diff --git a/examples/discover_customization_examples/public/plugin.tsx b/examples/discover_customization_examples/public/plugin.tsx index 7c35287b843ba..6dc6e8f48da58 100644 --- a/examples/discover_customization_examples/public/plugin.tsx +++ b/examples/discover_customization_examples/public/plugin.tsx @@ -7,17 +7,9 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { - EuiButton, - EuiContextMenu, - EuiFlexItem, - EuiPopover, - EuiWrappingPopover, - IconType, -} from '@elastic/eui'; +import { EuiButton, EuiContextMenu, EuiFlexItem, EuiPopover, IconType } from '@elastic/eui'; import { CoreSetup, CoreStart, Plugin, SimpleSavedObject } from '@kbn/core/public'; import type { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public'; -import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import type { CustomizationCallback, DiscoverSetup, @@ -102,112 +94,14 @@ export class DiscoverCustomizationExamplesPlugin implements Plugin { } start(core: CoreStart, plugins: DiscoverCustomizationExamplesStartPlugins) { - const { discover } = plugins; - - let isOptionsOpen = false; - const optionsContainer = document.createElement('div'); - const closeOptionsPopover = () => { - ReactDOM.unmountComponentAtNode(optionsContainer); - document.body.removeChild(optionsContainer); - isOptionsOpen = false; - }; - this.customizationCallback = ({ customizations, stateContainer }) => { customizations.set({ id: 'top_nav', defaultMenu: { newItem: { disabled: true }, openItem: { disabled: true }, - shareItem: { order: 200 }, alertsItem: { disabled: true }, inspectItem: { disabled: true }, - saveItem: { order: 400 }, - }, - getMenuItems: () => [ - { - data: { - id: 'options', - label: 'Options', - iconType: 'arrowDown', - iconSide: 'right', - testId: 'customOptionsButton', - run: (anchorElement: HTMLElement) => { - if (isOptionsOpen) { - closeOptionsPopover(); - return; - } - - isOptionsOpen = true; - document.body.appendChild(optionsContainer); - - const element = ( - - - alert('Create new clicked'), - }, - { - name: 'Make a copy', - icon: 'copy', - onClick: () => alert('Make a copy clicked'), - }, - { - name: 'Manage saved searches', - icon: 'gear', - onClick: () => alert('Manage saved searches clicked'), - }, - ], - }, - ]} - data-test-subj="customOptionsPopover" - /> - - - ); - - ReactDOM.render(element, optionsContainer); - }, - }, - order: 100, - }, - { - data: { - id: 'documentExplorer', - label: 'Document explorer', - iconType: 'discoverApp', - testId: 'documentExplorerButton', - run: () => { - discover.locator?.navigate({}); - }, - }, - order: 300, - }, - ], - getBadges: () => { - return [ - { - data: { - badgeText: 'Example badge', - color: 'warning', - }, - order: 10, - }, - ]; }, }); diff --git a/examples/discover_customization_examples/tsconfig.json b/examples/discover_customization_examples/tsconfig.json index 776153f943fac..30ff666575f1d 100644 --- a/examples/discover_customization_examples/tsconfig.json +++ b/examples/discover_customization_examples/tsconfig.json @@ -13,7 +13,6 @@ "@kbn/i18n-react", "@kbn/react-kibana-context-theme", "@kbn/data-plugin", - "@kbn/react-kibana-context-render", ], "exclude": ["target/**/*"] } diff --git a/examples/esql_ast_inspector/public/components/esql_inspector/helpers.tsx b/examples/esql_ast_inspector/public/components/esql_inspector/helpers.tsx index a117062f7efa9..19a0c54a722c6 100644 --- a/examples/esql_ast_inspector/public/components/esql_inspector/helpers.tsx +++ b/examples/esql_ast_inspector/public/components/esql_inspector/helpers.tsx @@ -82,6 +82,8 @@ export const highlight = (query: EsqlQuery): Annotation[] => { }); Walker.visitComments(query.ast, (comment) => { + if (!comment.location) return; + annotations.push([ comment.location.min, comment.location.max, diff --git a/examples/grid_example/public/app.tsx b/examples/grid_example/public/app.tsx index 332649720742a..0e73a76d790fd 100644 --- a/examples/grid_example/public/app.tsx +++ b/examples/grid_example/public/app.tsx @@ -7,53 +7,186 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import React from 'react'; +import { cloneDeep } from 'lodash'; +import React, { useRef, useState } from 'react'; import ReactDOM from 'react-dom'; -import { GridLayout, type GridLayoutData } from '@kbn/grid-layout'; +import { v4 as uuidv4 } from 'uuid'; + +import { + EuiBadge, + EuiButton, + EuiButtonEmpty, + EuiCallOut, + EuiFlexGroup, + EuiFlexItem, + EuiPageTemplate, + EuiProvider, + EuiSpacer, +} from '@elastic/eui'; import { AppMountParameters } from '@kbn/core-application-browser'; -import { EuiPageTemplate, EuiProvider } from '@elastic/eui'; +import { CoreStart } from '@kbn/core-lifecycle-browser'; +import { GridLayout, GridLayoutData, isLayoutEqual, type GridLayoutApi } from '@kbn/grid-layout'; +import { i18n } from '@kbn/i18n'; + +import { getPanelId } from './get_panel_id'; +import { + clearSerializedGridLayout, + getSerializedGridLayout, + setSerializedGridLayout, +} from './serialized_grid_layout'; + +const DASHBOARD_MARGIN_SIZE = 8; +const DASHBOARD_GRID_HEIGHT = 20; +const DASHBOARD_GRID_COLUMN_COUNT = 48; +const DEFAULT_PANEL_HEIGHT = 15; +const DEFAULT_PANEL_WIDTH = DASHBOARD_GRID_COLUMN_COUNT / 2; + +export const GridExample = ({ coreStart }: { coreStart: CoreStart }) => { + const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); + + const [layoutKey, setLayoutKey] = useState(uuidv4()); + const [gridLayoutApi, setGridLayoutApi] = useState(); + const savedLayout = useRef(getSerializedGridLayout()); + const currentLayout = useRef(savedLayout.current); -export const GridExample = () => { return ( - + + + { + clearSerializedGridLayout(); + window.location.reload(); + }} + > + {i18n.translate('examples.gridExample.resetExampleButton', { + defaultMessage: 'Reset example', + })} + + + + + + { + const panelId = await getPanelId({ + coreStart, + suggestion: `panel${(gridLayoutApi?.getPanelCount() ?? 0) + 1}`, + }); + if (panelId) + gridLayoutApi?.addPanel(panelId, { + width: DEFAULT_PANEL_WIDTH, + height: DEFAULT_PANEL_HEIGHT, + }); + }} + > + {i18n.translate('examples.gridExample.addPanelButton', { + defaultMessage: 'Add a panel', + })} + + + + + {hasUnsavedChanges && ( + + + {i18n.translate('examples.gridExample.unsavedChangesBadge', { + defaultMessage: 'Unsaved changes', + })} + + + )} + + { + currentLayout.current = cloneDeep(savedLayout.current); + setHasUnsavedChanges(false); + setLayoutKey(uuidv4()); // force remount of grid + }} + > + {i18n.translate('examples.gridExample.resetLayoutButton', { + defaultMessage: 'Reset', + })} + + + + { + if (gridLayoutApi) { + const layoutToSave = gridLayoutApi.serializeState(); + setSerializedGridLayout(layoutToSave); + savedLayout.current = layoutToSave; + setHasUnsavedChanges(false); + } + }} + > + {i18n.translate('examples.gridExample.saveLayoutButton', { + defaultMessage: 'Save', + })} + + + + + + { + currentLayout.current = cloneDeep(newLayout); + setHasUnsavedChanges(!isLayoutEqual(savedLayout.current, newLayout)); + }} + ref={setGridLayoutApi} renderPanelContents={(id) => { - return
{id}
; + return ( + <> +
{id}
+ { + gridLayoutApi?.removePanel(id); + }} + > + {i18n.translate('examples.gridExample.deletePanelButton', { + defaultMessage: 'Delete panel', + })} + + { + const newPanelId = await getPanelId({ + coreStart, + suggestion: `panel${(gridLayoutApi?.getPanelCount() ?? 0) + 1}`, + }); + if (newPanelId) gridLayoutApi?.replacePanel(id, newPanelId); + }} + > + {i18n.translate('examples.gridExample.replacePanelButton', { + defaultMessage: 'Replace panel', + })} + + + ); }} getCreationOptions={() => { - const initialLayout: GridLayoutData = [ - { - title: 'Large section', - isCollapsed: false, - panels: { - panel1: { column: 0, row: 0, width: 12, height: 6, id: 'panel1' }, - panel2: { column: 0, row: 6, width: 8, height: 4, id: 'panel2' }, - panel3: { column: 8, row: 6, width: 12, height: 4, id: 'panel3' }, - panel4: { column: 0, row: 10, width: 48, height: 4, id: 'panel4' }, - panel5: { column: 12, row: 0, width: 36, height: 6, id: 'panel5' }, - panel6: { column: 24, row: 6, width: 24, height: 4, id: 'panel6' }, - panel7: { column: 20, row: 6, width: 4, height: 2, id: 'panel7' }, - panel8: { column: 20, row: 8, width: 4, height: 2, id: 'panel8' }, - }, - }, - { - title: 'Small section', - isCollapsed: false, - panels: { panel9: { column: 0, row: 0, width: 12, height: 16, id: 'panel9' } }, - }, - { - title: 'Another small section', - isCollapsed: false, - panels: { panel10: { column: 24, row: 0, width: 12, height: 6, id: 'panel10' } }, - }, - ]; - return { - gridSettings: { gutterSize: 8, rowHeight: 26, columnCount: 48 }, - initialLayout, + gridSettings: { + gutterSize: DASHBOARD_MARGIN_SIZE, + rowHeight: DASHBOARD_GRID_HEIGHT, + columnCount: DASHBOARD_GRID_COLUMN_COUNT, + }, + initialLayout: cloneDeep(currentLayout.current), }; }} /> @@ -63,8 +196,11 @@ export const GridExample = () => { ); }; -export const renderGridExampleApp = (element: AppMountParameters['element']) => { - ReactDOM.render(, element); +export const renderGridExampleApp = ( + element: AppMountParameters['element'], + coreStart: CoreStart +) => { + ReactDOM.render(, element); return () => ReactDOM.unmountComponentAtNode(element); }; diff --git a/examples/grid_example/public/get_panel_id.tsx b/examples/grid_example/public/get_panel_id.tsx new file mode 100644 index 0000000000000..d83d0b232b53a --- /dev/null +++ b/examples/grid_example/public/get_panel_id.tsx @@ -0,0 +1,108 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React, { useState } from 'react'; + +import { + EuiButton, + EuiCallOut, + EuiFieldText, + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiSpacer, +} from '@elastic/eui'; +import { CoreStart } from '@kbn/core-lifecycle-browser'; +import { toMountPoint } from '@kbn/react-kibana-mount'; +import { i18n } from '@kbn/i18n'; + +const PanelIdModal = ({ + suggestion, + onClose, + onSubmit, +}: { + suggestion: string; + onClose: () => void; + onSubmit: (id: string) => void; +}) => { + const [panelId, setPanelId] = useState(suggestion); + + return ( + + + + {i18n.translate('examples.gridExample.getPanelIdModalTitle', { + defaultMessage: 'Panel ID', + })} + + + + + + + + { + setPanelId(e.target.value ?? ''); + }} + /> + + + { + onSubmit(panelId); + }} + > + {i18n.translate('examples.gridExample.getPanelIdSubmitButton', { + defaultMessage: 'Submit', + })} + + + + ); +}; + +export const getPanelId = async ({ + coreStart, + suggestion, +}: { + coreStart: CoreStart; + suggestion: string; +}): Promise => { + return new Promise((resolve) => { + const session = coreStart.overlays.openModal( + toMountPoint( + { + resolve(undefined); + session.close(); + }} + onSubmit={(newPanelId) => { + resolve(newPanelId); + session.close(); + }} + />, + { + theme: coreStart.theme, + i18n: coreStart.i18n, + } + ) + ); + }); +}; diff --git a/examples/grid_example/public/plugin.ts b/examples/grid_example/public/plugin.ts index 0f7d441a1be15..d57b06ac96017 100644 --- a/examples/grid_example/public/plugin.ts +++ b/examples/grid_example/public/plugin.ts @@ -26,8 +26,11 @@ export class GridExamplePlugin title: gridExampleTitle, visibleIn: [], async mount(params: AppMountParameters) { - const { renderGridExampleApp } = await import('./app'); - return renderGridExampleApp(params.element); + const [{ renderGridExampleApp }, [coreStart]] = await Promise.all([ + import('./app'), + core.getStartServices(), + ]); + return renderGridExampleApp(params.element, coreStart); }, }); developerExamples.register({ diff --git a/examples/grid_example/public/serialized_grid_layout.ts b/examples/grid_example/public/serialized_grid_layout.ts new file mode 100644 index 0000000000000..2bb20052398f8 --- /dev/null +++ b/examples/grid_example/public/serialized_grid_layout.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { type GridLayoutData } from '@kbn/grid-layout'; + +const STATE_SESSION_STORAGE_KEY = 'kibana.examples.gridExample.state'; + +export function clearSerializedGridLayout() { + sessionStorage.removeItem(STATE_SESSION_STORAGE_KEY); +} + +export function getSerializedGridLayout(): GridLayoutData { + const serializedStateJSON = sessionStorage.getItem(STATE_SESSION_STORAGE_KEY); + return serializedStateJSON ? JSON.parse(serializedStateJSON) : initialGridLayout; +} + +export function setSerializedGridLayout(layout: GridLayoutData) { + sessionStorage.setItem(STATE_SESSION_STORAGE_KEY, JSON.stringify(layout)); +} + +const initialGridLayout: GridLayoutData = [ + { + title: 'Large section', + isCollapsed: false, + panels: { + panel1: { column: 0, row: 0, width: 12, height: 6, id: 'panel1' }, + panel2: { column: 0, row: 6, width: 8, height: 4, id: 'panel2' }, + panel3: { column: 8, row: 6, width: 12, height: 4, id: 'panel3' }, + panel4: { column: 0, row: 10, width: 48, height: 4, id: 'panel4' }, + panel5: { column: 12, row: 0, width: 36, height: 6, id: 'panel5' }, + panel6: { column: 24, row: 6, width: 24, height: 4, id: 'panel6' }, + panel7: { column: 20, row: 6, width: 4, height: 2, id: 'panel7' }, + panel8: { column: 20, row: 8, width: 4, height: 2, id: 'panel8' }, + }, + }, + { + title: 'Small section', + isCollapsed: false, + panels: { panel9: { column: 0, row: 0, width: 12, height: 16, id: 'panel9' } }, + }, + { + title: 'Another small section', + isCollapsed: false, + panels: { panel10: { column: 24, row: 0, width: 12, height: 6, id: 'panel10' } }, + }, +]; diff --git a/examples/grid_example/tsconfig.json b/examples/grid_example/tsconfig.json index 23be45a74c2f7..ad692e9697b2d 100644 --- a/examples/grid_example/tsconfig.json +++ b/examples/grid_example/tsconfig.json @@ -10,5 +10,8 @@ "@kbn/core-application-browser", "@kbn/core", "@kbn/developer-examples-plugin", + "@kbn/core-lifecycle-browser", + "@kbn/react-kibana-mount", + "@kbn/i18n", ] } diff --git a/examples/response_stream/public/containers/app/pages/page_redux_stream/hooks.ts b/examples/response_stream/public/containers/app/pages/page_redux_stream/hooks.ts index f1c8c671611a8..735e70916593f 100644 --- a/examples/response_stream/public/containers/app/pages/page_redux_stream/hooks.ts +++ b/examples/response_stream/public/containers/app/pages/page_redux_stream/hooks.ts @@ -8,10 +8,9 @@ */ import type { TypedUseSelectorHook } from 'react-redux'; -import { useDispatch, useSelector, useStore } from 'react-redux'; -import type { AppDispatch, AppStore, RootState } from './store'; +import { useDispatch, useSelector } from 'react-redux'; +import type { AppDispatch, RootState } from './store'; // Use throughout your app instead of plain `useDispatch` and `useSelector` export const useAppDispatch: () => AppDispatch = useDispatch; export const useAppSelector: TypedUseSelectorHook = useSelector; -export const useAppStore: () => AppStore = useStore; diff --git a/oas_docs/bundle.json b/oas_docs/bundle.json index cd314d4c991bf..743d6ae6e422a 100644 --- a/oas_docs/bundle.json +++ b/oas_docs/bundle.json @@ -344,10 +344,10 @@ }, "openapi": "3.0.0", "paths": { - "/api/actions": { - "get": { - "deprecated": true, - "operationId": "%2Fapi%2Factions#0", + "/api/actions/connector/{id}": { + "delete": { + "description": "WARNING: When you delete a connector, it cannot be recovered.", + "operationId": "delete-actions-connector-id", "parameters": [ { "description": "The version of the API to use", @@ -360,19 +360,39 @@ ], "type": "string" } + }, + { + "description": "A required header to protect against CSRF attacks", + "in": "header", + "name": "kbn-xsrf", + "required": true, + "schema": { + "example": "true", + "type": "string" + } + }, + { + "description": "An identifier for the connector.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } } ], - "responses": {}, - "summary": "Get all connectors", + "responses": { + "204": { + "description": "Indicates a successful call." + } + }, + "summary": "Delete a connector", "tags": [ "connectors" ] - } - }, - "/api/actions/action": { - "post": { - "deprecated": true, - "operationId": "%2Fapi%2Factions%2Faction#0", + }, + "get": { + "operationId": "get-actions-connector-id", "parameters": [ { "description": "The version of the API to use", @@ -387,50 +407,15 @@ } }, { - "description": "A required header to protect against CSRF attacks", - "in": "header", - "name": "kbn-xsrf", + "description": "An identifier for the connector.", + "in": "path", + "name": "id", "required": true, "schema": { - "example": "true", "type": "string" } } ], - "requestBody": { - "content": { - "application/json; Elastic-Api-Version=2023-10-31": { - "schema": { - "additionalProperties": false, - "properties": { - "actionTypeId": { - "description": "The connector type identifier.", - "type": "string" - }, - "config": { - "additionalProperties": {}, - "default": {}, - "type": "object" - }, - "name": { - "description": "The display name for the connector.", - "type": "string" - }, - "secrets": { - "additionalProperties": {}, - "default": {}, - "type": "object" - } - }, - "required": [ - "name", - "actionTypeId" - ], - "type": "object" - } - } - } - }, "responses": { "200": { "content": { @@ -486,17 +471,13 @@ "description": "Indicates a successful call." } }, - "summary": "Create a connector", + "summary": "Get connector information", "tags": [ "connectors" ] - } - }, - "/api/actions/action/{id}": { - "delete": { - "deprecated": true, - "description": "WARNING: When you delete a connector, it cannot be recovered.", - "operationId": "%2Fapi%2Factions%2Faction%2F%7Bid%7D#0", + }, + "post": { + "operationId": "post-actions-connector-id", "parameters": [ { "description": "The version of the API to use", @@ -524,48 +505,46 @@ "description": "An identifier for the connector.", "in": "path", "name": "id", - "required": true, + "required": false, "schema": { "type": "string" } } ], - "responses": { - "204": { - "description": "Indicates a successful call." - } - }, - "summary": "Delete a connector", - "tags": [ - "connectors" - ] - }, - "get": { - "deprecated": true, - "operationId": "%2Fapi%2Factions%2Faction%2F%7Bid%7D#1", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - }, - { - "description": "An identifier for the connector.", - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" + "requestBody": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "properties": { + "config": { + "additionalProperties": {}, + "default": {}, + "type": "object" + }, + "connector_type_id": { + "description": "The type of connector.", + "type": "string" + }, + "name": { + "description": "The display name for the connector.", + "type": "string" + }, + "secrets": { + "additionalProperties": {}, + "default": {}, + "type": "object" + } + }, + "required": [ + "name", + "connector_type_id" + ], + "type": "object" + } } } - ], + }, "responses": { "200": { "content": { @@ -621,14 +600,13 @@ "description": "Indicates a successful call." } }, - "summary": "Get connector information", + "summary": "Create a connector", "tags": [ "connectors" ] }, "put": { - "deprecated": true, - "operationId": "%2Fapi%2Factions%2Faction%2F%7Bid%7D#2", + "operationId": "put-actions-connector-id", "parameters": [ { "description": "The version of the API to use", @@ -674,6 +652,7 @@ "type": "object" }, "name": { + "description": "The display name for the connector.", "type": "string" }, "secrets": { @@ -751,10 +730,10 @@ ] } }, - "/api/actions/action/{id}/_execute": { + "/api/actions/connector/{id}/_execute": { "post": { - "deprecated": true, - "operationId": "%2Fapi%2Factions%2Faction%2F%7Bid%7D%2F_execute#0", + "description": "You can use this API to test an action that involves interaction with Kibana services or integrations with third-party systems.", + "operationId": "post-actions-connector-id-execute", "parameters": [ { "description": "The version of the API to use", @@ -868,10 +847,10 @@ ] } }, - "/api/actions/connector/{id}": { - "delete": { - "description": "WARNING: When you delete a connector, it cannot be recovered.", - "operationId": "%2Fapi%2Factions%2Fconnector%2F%7Bid%7D#0", + "/api/actions/connector_types": { + "get": { + "description": "You do not need any Kibana feature privileges to run this API.", + "operationId": "get-actions-connector-types", "parameters": [ { "description": "The version of the API to use", @@ -886,37 +865,25 @@ } }, { - "description": "A required header to protect against CSRF attacks", - "in": "header", - "name": "kbn-xsrf", - "required": true, - "schema": { - "example": "true", - "type": "string" - } - }, - { - "description": "An identifier for the connector.", - "in": "path", - "name": "id", - "required": true, + "description": "A filter to limit the retrieved connector types to those that support a specific feature (such as alerting or cases).", + "in": "query", + "name": "feature_id", + "required": false, "schema": { "type": "string" } } ], - "responses": { - "204": { - "description": "Indicates a successful call." - } - }, - "summary": "Delete a connector", + "responses": {}, + "summary": "Get connector types", "tags": [ "connectors" ] - }, + } + }, + "/api/actions/connectors": { "get": { - "operationId": "%2Fapi%2Factions%2Fconnector%2F%7Bid%7D#1", + "operationId": "get-actions-connectors", "parameters": [ { "description": "The version of the API to use", @@ -929,534 +896,18 @@ ], "type": "string" } - }, - { - "description": "An identifier for the connector.", - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } } ], - "responses": { - "200": { - "content": { - "application/json; Elastic-Api-Version=2023-10-31": { - "schema": { - "additionalProperties": false, - "properties": { - "config": { - "additionalProperties": {}, - "type": "object" - }, - "connector_type_id": { - "description": "The connector type identifier.", - "type": "string" - }, - "id": { - "description": "The identifier for the connector.", - "type": "string" - }, - "is_deprecated": { - "description": "Indicates whether the connector is deprecated.", - "type": "boolean" - }, - "is_missing_secrets": { - "description": "Indicates whether the connector is missing secrets.", - "type": "boolean" - }, - "is_preconfigured": { - "description": "Indicates whether the connector is preconfigured. If true, the `config` and `is_missing_secrets` properties are omitted from the response. ", - "type": "boolean" - }, - "is_system_action": { - "description": "Indicates whether the connector is used for system actions.", - "type": "boolean" - }, - "name": { - "description": " The name of the rule.", - "type": "string" - } - }, - "required": [ - "id", - "name", - "connector_type_id", - "is_preconfigured", - "is_deprecated", - "is_system_action" - ], - "type": "object" - } - } - }, - "description": "Indicates a successful call." - } - }, - "summary": "Get connector information", + "responses": {}, + "summary": "Get all connectors", "tags": [ "connectors" ] - }, - "post": { - "operationId": "%2Fapi%2Factions%2Fconnector%2F%7Bid%3F%7D#0", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - }, - { - "description": "A required header to protect against CSRF attacks", - "in": "header", - "name": "kbn-xsrf", - "required": true, - "schema": { - "example": "true", - "type": "string" - } - }, - { - "description": "An identifier for the connector.", - "in": "path", - "name": "id", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json; Elastic-Api-Version=2023-10-31": { - "schema": { - "additionalProperties": false, - "properties": { - "config": { - "additionalProperties": {}, - "default": {}, - "type": "object" - }, - "connector_type_id": { - "description": "The type of connector.", - "type": "string" - }, - "name": { - "description": "The display name for the connector.", - "type": "string" - }, - "secrets": { - "additionalProperties": {}, - "default": {}, - "type": "object" - } - }, - "required": [ - "name", - "connector_type_id" - ], - "type": "object" - } - } - } - }, - "responses": { - "200": { - "content": { - "application/json; Elastic-Api-Version=2023-10-31": { - "schema": { - "additionalProperties": false, - "properties": { - "config": { - "additionalProperties": {}, - "type": "object" - }, - "connector_type_id": { - "description": "The connector type identifier.", - "type": "string" - }, - "id": { - "description": "The identifier for the connector.", - "type": "string" - }, - "is_deprecated": { - "description": "Indicates whether the connector is deprecated.", - "type": "boolean" - }, - "is_missing_secrets": { - "description": "Indicates whether the connector is missing secrets.", - "type": "boolean" - }, - "is_preconfigured": { - "description": "Indicates whether the connector is preconfigured. If true, the `config` and `is_missing_secrets` properties are omitted from the response. ", - "type": "boolean" - }, - "is_system_action": { - "description": "Indicates whether the connector is used for system actions.", - "type": "boolean" - }, - "name": { - "description": " The name of the rule.", - "type": "string" - } - }, - "required": [ - "id", - "name", - "connector_type_id", - "is_preconfigured", - "is_deprecated", - "is_system_action" - ], - "type": "object" - } - } - }, - "description": "Indicates a successful call." - } - }, - "summary": "Create a connector", - "tags": [ - "connectors" - ] - }, - "put": { - "operationId": "%2Fapi%2Factions%2Fconnector%2F%7Bid%7D#2", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - }, - { - "description": "A required header to protect against CSRF attacks", - "in": "header", - "name": "kbn-xsrf", - "required": true, - "schema": { - "example": "true", - "type": "string" - } - }, - { - "description": "An identifier for the connector.", - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json; Elastic-Api-Version=2023-10-31": { - "schema": { - "additionalProperties": false, - "properties": { - "config": { - "additionalProperties": {}, - "default": {}, - "type": "object" - }, - "name": { - "description": "The display name for the connector.", - "type": "string" - }, - "secrets": { - "additionalProperties": {}, - "default": {}, - "type": "object" - } - }, - "required": [ - "name" - ], - "type": "object" - } - } - } - }, - "responses": { - "200": { - "content": { - "application/json; Elastic-Api-Version=2023-10-31": { - "schema": { - "additionalProperties": false, - "properties": { - "config": { - "additionalProperties": {}, - "type": "object" - }, - "connector_type_id": { - "description": "The connector type identifier.", - "type": "string" - }, - "id": { - "description": "The identifier for the connector.", - "type": "string" - }, - "is_deprecated": { - "description": "Indicates whether the connector is deprecated.", - "type": "boolean" - }, - "is_missing_secrets": { - "description": "Indicates whether the connector is missing secrets.", - "type": "boolean" - }, - "is_preconfigured": { - "description": "Indicates whether the connector is preconfigured. If true, the `config` and `is_missing_secrets` properties are omitted from the response. ", - "type": "boolean" - }, - "is_system_action": { - "description": "Indicates whether the connector is used for system actions.", - "type": "boolean" - }, - "name": { - "description": " The name of the rule.", - "type": "string" - } - }, - "required": [ - "id", - "name", - "connector_type_id", - "is_preconfigured", - "is_deprecated", - "is_system_action" - ], - "type": "object" - } - } - }, - "description": "Indicates a successful call." - } - }, - "summary": "Update a connector", - "tags": [ - "connectors" - ] - } - }, - "/api/actions/connector/{id}/_execute": { - "post": { - "description": "You can use this API to test an action that involves interaction with Kibana services or integrations with third-party systems.", - "operationId": "%2Fapi%2Factions%2Fconnector%2F%7Bid%7D%2F_execute#0", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - }, - { - "description": "A required header to protect against CSRF attacks", - "in": "header", - "name": "kbn-xsrf", - "required": true, - "schema": { - "example": "true", - "type": "string" - } - }, - { - "description": "An identifier for the connector.", - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json; Elastic-Api-Version=2023-10-31": { - "schema": { - "additionalProperties": false, - "properties": { - "params": { - "additionalProperties": {}, - "type": "object" - } - }, - "required": [ - "params" - ], - "type": "object" - } - } - } - }, - "responses": { - "200": { - "content": { - "application/json; Elastic-Api-Version=2023-10-31": { - "schema": { - "additionalProperties": false, - "properties": { - "config": { - "additionalProperties": {}, - "type": "object" - }, - "connector_type_id": { - "description": "The connector type identifier.", - "type": "string" - }, - "id": { - "description": "The identifier for the connector.", - "type": "string" - }, - "is_deprecated": { - "description": "Indicates whether the connector is deprecated.", - "type": "boolean" - }, - "is_missing_secrets": { - "description": "Indicates whether the connector is missing secrets.", - "type": "boolean" - }, - "is_preconfigured": { - "description": "Indicates whether the connector is preconfigured. If true, the `config` and `is_missing_secrets` properties are omitted from the response. ", - "type": "boolean" - }, - "is_system_action": { - "description": "Indicates whether the connector is used for system actions.", - "type": "boolean" - }, - "name": { - "description": " The name of the rule.", - "type": "string" - } - }, - "required": [ - "id", - "name", - "connector_type_id", - "is_preconfigured", - "is_deprecated", - "is_system_action" - ], - "type": "object" - } - } - }, - "description": "Indicates a successful call." - } - }, - "summary": "Run a connector", - "tags": [ - "connectors" - ] - } - }, - "/api/actions/connector_types": { - "get": { - "description": "You do not need any Kibana feature privileges to run this API.", - "operationId": "%2Fapi%2Factions%2Fconnector_types#0", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - }, - { - "description": "A filter to limit the retrieved connector types to those that support a specific feature (such as alerting or cases).", - "in": "query", - "name": "feature_id", - "required": false, - "schema": { - "type": "string" - } - } - ], - "responses": {}, - "summary": "Get connector types", - "tags": [ - "connectors" - ] - } - }, - "/api/actions/connectors": { - "get": { - "operationId": "%2Fapi%2Factions%2Fconnectors#0", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - } - ], - "responses": {}, - "summary": "Get all connectors", - "tags": [ - "connectors" - ] - } - }, - "/api/actions/list_action_types": { - "get": { - "deprecated": true, - "operationId": "%2Fapi%2Factions%2Flist_action_types#0", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - } - ], - "responses": {}, - "summary": "Get connector types", - "tags": [ - "connectors" - ] - } - }, - "/api/alerting/rule/{id}": { - "delete": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Bid%7D#2", + } + }, + "/api/alerting/rule/{id}": { + "delete": { + "operationId": "delete-alerting-rule-id", "parameters": [ { "description": "The version of the API to use", @@ -1510,7 +961,7 @@ ] }, "get": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Bid%7D#0", + "operationId": "get-alerting-rule-id", "parameters": [ { "description": "The version of the API to use", @@ -2388,7 +1839,7 @@ ] }, "post": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Bid%3F%7D#0", + "operationId": "post-alerting-rule-id", "parameters": [ { "description": "The version of the API to use", @@ -3568,7 +3019,7 @@ ] }, "put": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Bid%7D#1", + "operationId": "put-alerting-rule-id", "parameters": [ { "description": "The version of the API to use", @@ -4736,7 +4187,7 @@ }, "/api/alerting/rule/{id}/_disable": { "post": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_disable#0", + "operationId": "post-alerting-rule-id-disable", "parameters": [ { "description": "The version of the API to use", @@ -4810,7 +4261,7 @@ }, "/api/alerting/rule/{id}/_enable": { "post": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_enable#0", + "operationId": "post-alerting-rule-id-enable", "parameters": [ { "description": "The version of the API to use", @@ -4866,7 +4317,7 @@ }, "/api/alerting/rule/{id}/_mute_all": { "post": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_mute_all#0", + "operationId": "post-alerting-rule-id-mute-all", "parameters": [ { "description": "The version of the API to use", @@ -4922,7 +4373,7 @@ }, "/api/alerting/rule/{id}/_unmute_all": { "post": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_unmute_all#0", + "operationId": "post-alerting-rule-id-unmute-all", "parameters": [ { "description": "The version of the API to use", @@ -4978,7 +4429,7 @@ }, "/api/alerting/rule/{id}/_update_api_key": { "post": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_update_api_key#0", + "operationId": "post-alerting-rule-id-update-api-key", "parameters": [ { "description": "The version of the API to use", @@ -5037,7 +4488,7 @@ }, "/api/alerting/rule/{rule_id}/alert/{alert_id}/_mute": { "post": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Brule_id%7D%2Falert%2F%7Balert_id%7D%2F_mute#0", + "operationId": "post-alerting-rule-rule-id-alert-alert-id-mute", "parameters": [ { "description": "The version of the API to use", @@ -5102,7 +4553,7 @@ }, "/api/alerting/rule/{rule_id}/alert/{alert_id}/_unmute": { "post": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Brule_id%7D%2Falert%2F%7Balert_id%7D%2F_unmute#0", + "operationId": "post-alerting-rule-rule-id-alert-alert-id-unmute", "parameters": [ { "description": "The version of the API to use", @@ -5167,7 +4618,7 @@ }, "/api/alerting/rules/_find": { "get": { - "operationId": "%2Fapi%2Falerting%2Frules%2F_find#0", + "operationId": "get-alerting-rules-find", "parameters": [ { "description": "The version of the API to use", @@ -6175,67 +5626,10 @@ ] } }, - "/api/fleet/agent-status": { - "get": { - "operationId": "%2Fapi%2Ffleet%2Fagent-status#0", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - }, - { - "in": "query", - "name": "policyId", - "required": false, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "policyIds", - "required": false, - "schema": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ] - } - }, - { - "in": "query", - "name": "kuery", - "required": false, - "schema": { - "deprecated": true, - "type": "string" - } - } - ], - "responses": {}, - "summary": "", - "tags": [] - } - }, "/api/fleet/agent_download_sources": { "get": { "description": "List agent binary download sources", - "operationId": "%2Fapi%2Ffleet%2Fagent_download_sources#0", + "operationId": "get-fleet-agent-download-sources", "parameters": [ { "description": "The version of the API to use", @@ -6344,7 +5738,7 @@ }, "post": { "description": "Create agent binary download source", - "operationId": "%2Fapi%2Ffleet%2Fagent_download_sources#1", + "operationId": "post-fleet-agent-download-sources", "parameters": [ { "description": "The version of the API to use", @@ -6485,7 +5879,7 @@ "/api/fleet/agent_download_sources/{sourceId}": { "delete": { "description": "Delete agent binary download source by ID", - "operationId": "%2Fapi%2Ffleet%2Fagent_download_sources%2F%7BsourceId%7D#2", + "operationId": "delete-fleet-agent-download-sources-sourceid", "parameters": [ { "description": "The version of the API to use", @@ -6570,7 +5964,7 @@ }, "get": { "description": "Get agent binary download source by ID", - "operationId": "%2Fapi%2Ffleet%2Fagent_download_sources%2F%7BsourceId%7D#0", + "operationId": "get-fleet-agent-download-sources-sourceid", "parameters": [ { "description": "The version of the API to use", @@ -6672,7 +6066,7 @@ }, "put": { "description": "Update agent binary download source by ID", - "operationId": "%2Fapi%2Ffleet%2Fagent_download_sources%2F%7BsourceId%7D#1", + "operationId": "put-fleet-agent-download-sources-sourceid", "parameters": [ { "description": "The version of the API to use", @@ -6821,7 +6215,7 @@ "/api/fleet/agent_policies": { "get": { "description": "List agent policies", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies#0", + "operationId": "get-fleet-agent-policies", "parameters": [ { "description": "The version of the API to use", @@ -7659,7 +7053,7 @@ }, "post": { "description": "Create an agent policy", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies#1", + "operationId": "post-fleet-agent-policies", "parameters": [ { "description": "The version of the API to use", @@ -8654,7 +8048,7 @@ "/api/fleet/agent_policies/_bulk_get": { "post": { "description": "Bulk get agent policies", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2F_bulk_get#0", + "operationId": "post-fleet-agent-policies-bulk-get", "parameters": [ { "description": "The version of the API to use", @@ -9441,7 +8835,7 @@ "/api/fleet/agent_policies/delete": { "post": { "description": "Delete agent policy by ID", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2Fdelete#0", + "operationId": "post-fleet-agent-policies-delete", "parameters": [ { "description": "The version of the API to use", @@ -9546,7 +8940,7 @@ "/api/fleet/agent_policies/outputs": { "post": { "description": "Get list of outputs associated with agent policies", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2Foutputs#0", + "operationId": "post-fleet-agent-policies-outputs", "parameters": [ { "description": "The version of the API to use", @@ -9731,7 +9125,7 @@ "/api/fleet/agent_policies/{agentPolicyId}": { "get": { "description": "Get an agent policy by ID", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D#0", + "operationId": "get-fleet-agent-policies-agentpolicyid", "parameters": [ { "description": "The version of the API to use", @@ -10482,7 +9876,7 @@ }, "put": { "description": "Update an agent policy by ID", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D#1", + "operationId": "put-fleet-agent-policies-agentpolicyid", "parameters": [ { "description": "The version of the API to use", @@ -11489,7 +10883,7 @@ "/api/fleet/agent_policies/{agentPolicyId}/copy": { "post": { "description": "Copy an agent policy by ID", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Fcopy#0", + "operationId": "post-fleet-agent-policies-agentpolicyid-copy", "parameters": [ { "description": "The version of the API to use", @@ -12274,7 +11668,7 @@ "/api/fleet/agent_policies/{agentPolicyId}/download": { "get": { "description": "Download an agent policy by ID", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Fdownload#0", + "operationId": "get-fleet-agent-policies-agentpolicyid-download", "parameters": [ { "description": "The version of the API to use", @@ -12391,7 +11785,7 @@ "/api/fleet/agent_policies/{agentPolicyId}/full": { "get": { "description": "Get a full agent policy by ID", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Ffull#0", + "operationId": "get-fleet-agent-policies-agentpolicyid-full", "parameters": [ { "description": "The version of the API to use", @@ -12893,7 +12287,7 @@ "/api/fleet/agent_policies/{agentPolicyId}/outputs": { "get": { "description": "Get list of outputs associated with agent policy by policy id", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Foutputs#0", + "operationId": "get-fleet-agent-policies-agentpolicyid-outputs", "parameters": [ { "description": "The version of the API to use", @@ -13051,7 +12445,7 @@ "/api/fleet/agent_status": { "get": { "description": "Get agent status summary", - "operationId": "%2Fapi%2Ffleet%2Fagent_status#0", + "operationId": "get-fleet-agent-status", "parameters": [ { "description": "The version of the API to use", @@ -13096,7 +12490,6 @@ "name": "kuery", "required": false, "schema": { - "deprecated": true, "type": "string" } } @@ -13135,10 +12528,6 @@ "other": { "type": "number" }, - "total": { - "deprecated": true, - "type": "number" - }, "unenrolled": { "type": "number" }, @@ -13148,7 +12537,6 @@ }, "required": [ "events", - "total", "online", "error", "offline", @@ -13205,7 +12593,7 @@ "/api/fleet/agent_status/data": { "get": { "description": "Get incoming agent data", - "operationId": "%2Fapi%2Ffleet%2Fagent_status%2Fdata#0", + "operationId": "get-fleet-agent-status-data", "parameters": [ { "description": "The version of the API to use", @@ -13321,7 +12709,7 @@ "/api/fleet/agents": { "get": { "description": "List agents", - "operationId": "%2Fapi%2Ffleet%2Fagents#0", + "operationId": "get-fleet-agents", "parameters": [ { "description": "The version of the API to use", @@ -13812,394 +13200,6 @@ }, "type": "array" }, - "list": { - "deprecated": true, - "items": { - "additionalProperties": false, - "properties": { - "access_api_key": { - "type": "string" - }, - "access_api_key_id": { - "type": "string" - }, - "active": { - "type": "boolean" - }, - "agent": { - "additionalProperties": true, - "properties": { - "id": { - "type": "string" - }, - "version": { - "type": "string" - } - }, - "required": [ - "id", - "version" - ], - "type": "object" - }, - "components": { - "items": { - "additionalProperties": false, - "properties": { - "id": { - "type": "string" - }, - "message": { - "type": "string" - }, - "status": { - "enum": [ - "STARTING", - "CONFIGURING", - "HEALTHY", - "DEGRADED", - "FAILED", - "STOPPING", - "STOPPED" - ], - "type": "string" - }, - "type": { - "type": "string" - }, - "units": { - "items": { - "additionalProperties": false, - "properties": { - "id": { - "type": "string" - }, - "message": { - "type": "string" - }, - "payload": { - "additionalProperties": {}, - "type": "object" - }, - "status": { - "enum": [ - "STARTING", - "CONFIGURING", - "HEALTHY", - "DEGRADED", - "FAILED", - "STOPPING", - "STOPPED" - ], - "type": "string" - }, - "type": { - "enum": [ - "input", - "output" - ], - "type": "string" - } - }, - "required": [ - "id", - "type", - "status", - "message" - ], - "type": "object" - }, - "type": "array" - } - }, - "required": [ - "id", - "type", - "status", - "message" - ], - "type": "object" - }, - "type": "array" - }, - "default_api_key": { - "type": "string" - }, - "default_api_key_history": { - "items": { - "additionalProperties": false, - "deprecated": true, - "properties": { - "id": { - "type": "string" - }, - "retired_at": { - "type": "string" - } - }, - "required": [ - "id", - "retired_at" - ], - "type": "object" - }, - "type": "array" - }, - "default_api_key_id": { - "type": "string" - }, - "enrolled_at": { - "type": "string" - }, - "id": { - "type": "string" - }, - "last_checkin": { - "type": "string" - }, - "last_checkin_message": { - "type": "string" - }, - "last_checkin_status": { - "enum": [ - "error", - "online", - "degraded", - "updating", - "starting" - ], - "type": "string" - }, - "local_metadata": { - "additionalProperties": {}, - "type": "object" - }, - "metrics": { - "additionalProperties": false, - "properties": { - "cpu_avg": { - "type": "number" - }, - "memory_size_byte_avg": { - "type": "number" - } - }, - "type": "object" - }, - "namespaces": { - "items": { - "type": "string" - }, - "type": "array" - }, - "outputs": { - "additionalProperties": { - "additionalProperties": false, - "properties": { - "api_key_id": { - "type": "string" - }, - "to_retire_api_key_ids": { - "items": { - "additionalProperties": false, - "properties": { - "id": { - "type": "string" - }, - "retired_at": { - "type": "string" - } - }, - "required": [ - "id", - "retired_at" - ], - "type": "object" - }, - "type": "array" - }, - "type": { - "type": "string" - } - }, - "required": [ - "api_key_id", - "type" - ], - "type": "object" - }, - "type": "object" - }, - "packages": { - "items": { - "type": "string" - }, - "type": "array" - }, - "policy_id": { - "type": "string" - }, - "policy_revision": { - "nullable": true, - "type": "number" - }, - "sort": { - "items": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "string" - }, - { - "enum": [], - "nullable": true - } - ] - }, - "type": "array" - }, - "status": { - "enum": [ - "offline", - "error", - "online", - "inactive", - "enrolling", - "unenrolling", - "unenrolled", - "updating", - "degraded" - ], - "type": "string" - }, - "tags": { - "items": { - "type": "string" - }, - "type": "array" - }, - "type": { - "enum": [ - "PERMANENT", - "EPHEMERAL", - "TEMPORARY" - ], - "type": "string" - }, - "unenrolled_at": { - "type": "string" - }, - "unenrollment_started_at": { - "type": "string" - }, - "unhealthy_reason": { - "items": { - "enum": [ - "input", - "output", - "other" - ], - "type": "string" - }, - "nullable": true, - "type": "array" - }, - "upgrade_details": { - "additionalProperties": false, - "properties": { - "action_id": { - "type": "string" - }, - "metadata": { - "additionalProperties": false, - "properties": { - "download_percent": { - "type": "number" - }, - "download_rate": { - "type": "number" - }, - "error_msg": { - "type": "string" - }, - "failed_state": { - "enum": [ - "UPG_REQUESTED", - "UPG_SCHEDULED", - "UPG_DOWNLOADING", - "UPG_EXTRACTING", - "UPG_REPLACING", - "UPG_RESTARTING", - "UPG_FAILED", - "UPG_WATCHING", - "UPG_ROLLBACK" - ], - "type": "string" - }, - "retry_error_msg": { - "type": "string" - }, - "retry_until": { - "type": "string" - }, - "scheduled_at": { - "type": "string" - } - }, - "type": "object" - }, - "state": { - "enum": [ - "UPG_REQUESTED", - "UPG_SCHEDULED", - "UPG_DOWNLOADING", - "UPG_EXTRACTING", - "UPG_REPLACING", - "UPG_RESTARTING", - "UPG_FAILED", - "UPG_WATCHING", - "UPG_ROLLBACK" - ], - "type": "string" - }, - "target_version": { - "type": "string" - } - }, - "required": [ - "target_version", - "action_id", - "state" - ], - "type": "object" - }, - "upgrade_started_at": { - "nullable": true, - "type": "string" - }, - "upgraded_at": { - "nullable": true, - "type": "string" - }, - "user_provided_metadata": { - "additionalProperties": {}, - "type": "object" - } - }, - "required": [ - "id", - "packages", - "type", - "active", - "enrolled_at", - "local_metadata" - ], - "type": "object" - }, - "type": "array" - }, "page": { "type": "number" }, @@ -14260,7 +13260,7 @@ }, "post": { "description": "List agents by action ids", - "operationId": "%2Fapi%2Ffleet%2Fagents#1", + "operationId": "post-fleet-agents", "parameters": [ { "description": "The version of the API to use", @@ -14363,7 +13363,7 @@ "/api/fleet/agents/action_status": { "get": { "description": "Get agent action status", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Faction_status#0", + "operationId": "get-fleet-agents-action-status", "parameters": [ { "description": "The version of the API to use", @@ -14599,7 +13599,7 @@ "/api/fleet/agents/actions/{actionId}/cancel": { "post": { "description": "Cancel agent action", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Factions%2F%7BactionId%7D%2Fcancel#0", + "operationId": "post-fleet-agents-actions-actionid-cancel", "parameters": [ { "description": "The version of the API to use", @@ -14741,7 +13741,7 @@ "/api/fleet/agents/available_versions": { "get": { "description": "Get available agent versions", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Favailable_versions#0", + "operationId": "get-fleet-agents-available-versions", "parameters": [ { "description": "The version of the API to use", @@ -14813,7 +13813,7 @@ "/api/fleet/agents/bulk_reassign": { "post": { "description": "Bulk reassign agents", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Fbulk_reassign#0", + "operationId": "post-fleet-agents-bulk-reassign", "parameters": [ { "description": "The version of the API to use", @@ -14931,7 +13931,7 @@ "/api/fleet/agents/bulk_request_diagnostics": { "post": { "description": "Bulk request diagnostics from agents", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Fbulk_request_diagnostics#0", + "operationId": "post-fleet-agents-bulk-request-diagnostics", "parameters": [ { "description": "The version of the API to use", @@ -15050,7 +14050,7 @@ "/api/fleet/agents/bulk_unenroll": { "post": { "description": "Bulk unenroll agents", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Fbulk_unenroll#0", + "operationId": "post-fleet-agents-bulk-unenroll", "parameters": [ { "description": "The version of the API to use", @@ -15174,7 +14174,7 @@ "/api/fleet/agents/bulk_update_agent_tags": { "post": { "description": "Bulk update agent tags", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Fbulk_update_agent_tags#0", + "operationId": "post-fleet-agents-bulk-update-agent-tags", "parameters": [ { "description": "The version of the API to use", @@ -15300,7 +14300,7 @@ "/api/fleet/agents/bulk_upgrade": { "post": { "description": "Bulk upgrade agents", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Fbulk_upgrade#0", + "operationId": "post-fleet-agents-bulk-upgrade", "parameters": [ { "description": "The version of the API to use", @@ -15434,7 +14434,7 @@ "/api/fleet/agents/files/{fileId}": { "delete": { "description": "Delete file uploaded by agent", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Ffiles%2F%7BfileId%7D#0", + "operationId": "delete-fleet-agents-files-fileid", "parameters": [ { "description": "The version of the API to use", @@ -15525,7 +14525,7 @@ "/api/fleet/agents/files/{fileId}/{fileName}": { "get": { "description": "Get file uploaded by agent", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Ffiles%2F%7BfileId%7D%2F%7BfileName%7D#0", + "operationId": "get-fleet-agents-files-fileid-filename", "parameters": [ { "description": "The version of the API to use", @@ -15601,7 +14601,7 @@ "/api/fleet/agents/setup": { "get": { "description": "Get agent setup info", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Fsetup#0", + "operationId": "get-fleet-agents-setup", "parameters": [ { "description": "The version of the API to use", @@ -15702,7 +14702,7 @@ }, "post": { "description": "Initiate agent setup", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Fsetup#1", + "operationId": "post-fleet-agents-setup", "parameters": [ { "description": "The version of the API to use", @@ -15802,7 +14802,7 @@ "/api/fleet/agents/tags": { "get": { "description": "List agent tags", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Ftags#0", + "operationId": "get-fleet-agents-tags", "parameters": [ { "description": "The version of the API to use", @@ -15891,7 +14891,7 @@ "/api/fleet/agents/{agentId}": { "delete": { "description": "Delete agent by ID", - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D#2", + "operationId": "delete-fleet-agents-agentid", "parameters": [ { "description": "The version of the API to use", @@ -15979,7 +14979,7 @@ }, "get": { "description": "Get agent by ID", - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D#0", + "operationId": "get-fleet-agents-agentid", "parameters": [ { "description": "The version of the API to use", @@ -16444,7 +15444,7 @@ }, "put": { "description": "Update agent by ID", - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D#1", + "operationId": "put-fleet-agents-agentid", "parameters": [ { "description": "The version of the API to use", @@ -16934,7 +15934,7 @@ "/api/fleet/agents/{agentId}/actions": { "post": { "description": "Create agent action", - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Factions#0", + "operationId": "post-fleet-agents-agentid-actions", "parameters": [ { "description": "The version of the API to use", @@ -17151,7 +16151,7 @@ "/api/fleet/agents/{agentId}/reassign": { "post": { "description": "Reassign agent", - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Freassign#1", + "operationId": "post-fleet-agents-agentid-reassign", "parameters": [ { "description": "The version of the API to use", @@ -17244,68 +16244,12 @@ "tags": [ "Elastic Agent actions" ] - }, - "put": { - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Freassign#0", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - }, - { - "description": "A required header to protect against CSRF attacks", - "in": "header", - "name": "kbn-xsrf", - "required": true, - "schema": { - "example": "true", - "type": "string" - } - }, - { - "in": "path", - "name": "agentId", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json; Elastic-Api-Version=2023-10-31": { - "schema": { - "additionalProperties": false, - "properties": { - "policy_id": { - "type": "string" - } - }, - "required": [ - "policy_id" - ], - "type": "object" - } - } - } - }, - "responses": {}, - "summary": "", - "tags": [] } }, "/api/fleet/agents/{agentId}/request_diagnostics": { "post": { "description": "Request agent diagnostics", - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Frequest_diagnostics#0", + "operationId": "post-fleet-agents-agentid-request-diagnostics", "parameters": [ { "description": "The version of the API to use", @@ -17414,7 +16358,7 @@ "/api/fleet/agents/{agentId}/unenroll": { "post": { "description": "Unenroll agent", - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Funenroll#0", + "operationId": "post-fleet-agents-agentid-unenroll", "parameters": [ { "description": "The version of the API to use", @@ -17476,7 +16420,7 @@ "/api/fleet/agents/{agentId}/upgrade": { "post": { "description": "Upgrade agent", - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Fupgrade#0", + "operationId": "post-fleet-agents-agentid-upgrade", "parameters": [ { "description": "The version of the API to use", @@ -17583,7 +16527,7 @@ "/api/fleet/agents/{agentId}/uploads": { "get": { "description": "List agent uploads", - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Fuploads#0", + "operationId": "get-fleet-agents-agentid-uploads", "parameters": [ { "description": "The version of the API to use", @@ -17703,7 +16647,7 @@ "/api/fleet/check-permissions": { "get": { "description": "Check permissions", - "operationId": "%2Fapi%2Ffleet%2Fcheck-permissions#0", + "operationId": "get-fleet-check-permissions", "parameters": [ { "description": "The version of the API to use", @@ -17788,7 +16732,7 @@ "/api/fleet/data_streams": { "get": { "description": "List data streams", - "operationId": "%2Fapi%2Ffleet%2Fdata_streams#0", + "operationId": "get-fleet-data-streams", "parameters": [ { "description": "The version of the API to use", @@ -17945,7 +16889,7 @@ }, "/api/fleet/enrollment-api-keys": { "get": { - "operationId": "%2Fapi%2Ffleet%2Fenrollment-api-keys#0", + "operationId": "get-fleet-enrollment-api-keys-2", "parameters": [ { "description": "The version of the API to use", @@ -17991,7 +16935,7 @@ "tags": [] }, "post": { - "operationId": "%2Fapi%2Ffleet%2Fenrollment-api-keys#1", + "operationId": "post-fleet-enrollment-api-keys-2", "parameters": [ { "description": "The version of the API to use", @@ -18047,7 +16991,7 @@ }, "/api/fleet/enrollment-api-keys/{keyId}": { "delete": { - "operationId": "%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#1", + "operationId": "delete-fleet-enrollment-api-keys-keyid-2", "parameters": [ { "description": "The version of the API to use", @@ -18085,7 +17029,7 @@ "tags": [] }, "get": { - "operationId": "%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#0", + "operationId": "get-fleet-enrollment-api-keys-keyid-2", "parameters": [ { "description": "The version of the API to use", @@ -18116,7 +17060,7 @@ "/api/fleet/enrollment_api_keys": { "get": { "description": "List enrollment API keys", - "operationId": "%2Fapi%2Ffleet%2Fenrollment_api_keys#0", + "operationId": "get-fleet-enrollment-api-keys", "parameters": [ { "description": "The version of the API to use", @@ -18304,7 +17248,7 @@ }, "post": { "description": "Create enrollment API key", - "operationId": "%2Fapi%2Ffleet%2Fenrollment_api_keys#1", + "operationId": "post-fleet-enrollment-api-keys", "parameters": [ { "description": "The version of the API to use", @@ -18450,7 +17394,7 @@ "/api/fleet/enrollment_api_keys/{keyId}": { "delete": { "description": "Revoke enrollment API key by ID by marking it as inactive", - "operationId": "%2Fapi%2Ffleet%2Fenrollment_api_keys%2F%7BkeyId%7D#1", + "operationId": "delete-fleet-enrollment-api-keys-keyid", "parameters": [ { "description": "The version of the API to use", @@ -18538,7 +17482,7 @@ }, "get": { "description": "Get enrollment API key by ID", - "operationId": "%2Fapi%2Ffleet%2Fenrollment_api_keys%2F%7BkeyId%7D#0", + "operationId": "get-fleet-enrollment-api-keys-keyid", "parameters": [ { "description": "The version of the API to use", @@ -18651,7 +17595,7 @@ "/api/fleet/epm/bulk_assets": { "post": { "description": "Bulk get assets", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fbulk_assets#0", + "operationId": "post-fleet-epm-bulk-assets", "parameters": [ { "description": "The version of the API to use", @@ -18802,7 +17746,7 @@ "/api/fleet/epm/categories": { "get": { "description": "List package categories", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fcategories#0", + "operationId": "get-fleet-epm-categories", "parameters": [ { "description": "The version of the API to use", @@ -18951,7 +17895,7 @@ "/api/fleet/epm/custom_integrations": { "post": { "description": "Create custom integration", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fcustom_integrations#0", + "operationId": "post-fleet-epm-custom-integrations", "parameters": [ { "description": "The version of the API to use", @@ -19233,7 +18177,7 @@ "/api/fleet/epm/data_streams": { "get": { "description": "List data streams", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fdata_streams#0", + "operationId": "get-fleet-epm-data-streams", "parameters": [ { "description": "The version of the API to use", @@ -19359,7 +18303,7 @@ "/api/fleet/epm/packages": { "get": { "description": "List packages", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages#0", + "operationId": "get-fleet-epm-packages", "parameters": [ { "description": "The version of the API to use", @@ -20428,7 +19372,7 @@ }, "post": { "description": "Install package by upload", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages#1", + "operationId": "post-fleet-epm-packages", "parameters": [ { "description": "The version of the API to use", @@ -20690,7 +19634,7 @@ "/api/fleet/epm/packages/_bulk": { "post": { "description": "Bulk install packages", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F_bulk#0", + "operationId": "post-fleet-epm-packages-bulk", "parameters": [ { "description": "The version of the API to use", @@ -21114,7 +20058,7 @@ "/api/fleet/epm/packages/installed": { "get": { "description": "Get installed packages", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2Finstalled#0", + "operationId": "get-fleet-epm-packages-installed", "parameters": [ { "description": "The version of the API to use", @@ -21355,7 +20299,7 @@ "/api/fleet/epm/packages/limited": { "get": { "description": "Get limited package list", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2Flimited#0", + "operationId": "get-fleet-epm-packages-limited", "parameters": [ { "description": "The version of the API to use", @@ -21434,7 +20378,7 @@ "/api/fleet/epm/packages/{pkgName}/stats": { "get": { "description": "Get package stats", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2Fstats#0", + "operationId": "get-fleet-epm-packages-pkgname-stats", "parameters": [ { "description": "The version of the API to use", @@ -21520,7 +20464,7 @@ "/api/fleet/epm/packages/{pkgName}/{pkgVersion}": { "delete": { "description": "Delete package", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D#3", + "operationId": "delete-fleet-epm-packages-pkgname-pkgversion", "parameters": [ { "description": "The version of the API to use", @@ -21782,7 +20726,7 @@ }, "get": { "description": "Get package", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D#0", + "operationId": "get-fleet-epm-packages-pkgname-pkgversion", "parameters": [ { "description": "The version of the API to use", @@ -23046,7 +21990,7 @@ }, "post": { "description": "Install package from registry", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D#2", + "operationId": "post-fleet-epm-packages-pkgname-pkgversion", "parameters": [ { "description": "The version of the API to use", @@ -23341,7 +22285,7 @@ }, "put": { "description": "Update package settings", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D#1", + "operationId": "put-fleet-epm-packages-pkgname-pkgversion", "parameters": [ { "description": "The version of the API to use", @@ -24590,7 +23534,7 @@ "/api/fleet/epm/packages/{pkgName}/{pkgVersion}/transforms/authorize": { "post": { "description": "Authorize transforms", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D%2Ftransforms%2Fauthorize#0", + "operationId": "post-fleet-epm-packages-pkgname-pkgversion-transforms-authorize", "parameters": [ { "description": "The version of the API to use", @@ -24734,7 +23678,7 @@ "/api/fleet/epm/packages/{pkgName}/{pkgVersion}/{filePath*}": { "get": { "description": "Get package file", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D%2F%7BfilePath*%7D#0", + "operationId": "get-fleet-epm-packages-pkgname-pkgversion-filepath", "parameters": [ { "description": "The version of the API to use", @@ -24815,7 +23759,7 @@ }, "/api/fleet/epm/packages/{pkgkey}": { "delete": { - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#3", + "operationId": "delete-fleet-epm-packages-pkgkey", "parameters": [ { "description": "The version of the API to use", @@ -24872,7 +23816,7 @@ "tags": [] }, "get": { - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#0", + "operationId": "get-fleet-epm-packages-pkgkey", "parameters": [ { "description": "The version of the API to use", @@ -24933,7 +23877,7 @@ "tags": [] }, "post": { - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#2", + "operationId": "post-fleet-epm-packages-pkgkey", "parameters": [ { "description": "The version of the API to use", @@ -25016,7 +23960,7 @@ "tags": [] }, "put": { - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#1", + "operationId": "put-fleet-epm-packages-pkgkey", "parameters": [ { "description": "The version of the API to use", @@ -25075,7 +24019,7 @@ "/api/fleet/epm/templates/{pkgName}/{pkgVersion}/inputs": { "get": { "description": "Get inputs template", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Ftemplates%2F%7BpkgName%7D%2F%7BpkgVersion%7D%2Finputs#0", + "operationId": "get-fleet-epm-templates-pkgname-pkgversion-inputs", "parameters": [ { "description": "The version of the API to use", @@ -25244,7 +24188,7 @@ "/api/fleet/epm/verification_key_id": { "get": { "description": "Get a package signature verification key ID", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fverification_key_id#0", + "operationId": "get-fleet-epm-verification-key-id", "parameters": [ { "description": "The version of the API to use", @@ -25314,7 +24258,7 @@ "/api/fleet/fleet_server_hosts": { "get": { "description": "List Fleet Server hosts", - "operationId": "%2Fapi%2Ffleet%2Ffleet_server_hosts#0", + "operationId": "get-fleet-fleet-server-hosts", "parameters": [ { "description": "The version of the API to use", @@ -25432,7 +24376,7 @@ }, "post": { "description": "Create Fleet Server host", - "operationId": "%2Fapi%2Ffleet%2Ffleet_server_hosts#1", + "operationId": "post-fleet-fleet-server-hosts", "parameters": [ { "description": "The version of the API to use", @@ -25591,7 +24535,7 @@ "/api/fleet/fleet_server_hosts/{itemId}": { "delete": { "description": "Delete Fleet Server host by ID", - "operationId": "%2Fapi%2Ffleet%2Ffleet_server_hosts%2F%7BitemId%7D#1", + "operationId": "delete-fleet-fleet-server-hosts-itemid", "parameters": [ { "description": "The version of the API to use", @@ -25676,7 +24620,7 @@ }, "get": { "description": "Get Fleet Server host by ID", - "operationId": "%2Fapi%2Ffleet%2Ffleet_server_hosts%2F%7BitemId%7D#0", + "operationId": "get-fleet-fleet-server-hosts-itemid", "parameters": [ { "description": "The version of the API to use", @@ -25787,7 +24731,7 @@ }, "put": { "description": "Update Fleet Server host by ID", - "operationId": "%2Fapi%2Ffleet%2Ffleet_server_hosts%2F%7BitemId%7D#2", + "operationId": "put-fleet-fleet-server-hosts-itemid", "parameters": [ { "description": "The version of the API to use", @@ -25945,7 +24889,7 @@ "/api/fleet/health_check": { "post": { "description": "Check Fleet Server health", - "operationId": "%2Fapi%2Ffleet%2Fhealth_check#0", + "operationId": "post-fleet-health-check", "parameters": [ { "description": "The version of the API to use", @@ -26081,7 +25025,7 @@ "/api/fleet/kubernetes": { "get": { "description": "Get full K8s agent manifest", - "operationId": "%2Fapi%2Ffleet%2Fkubernetes#0", + "operationId": "get-fleet-kubernetes", "parameters": [ { "description": "The version of the API to use", @@ -26173,7 +25117,7 @@ }, "/api/fleet/kubernetes/download": { "get": { - "operationId": "%2Fapi%2Ffleet%2Fkubernetes%2Fdownload#0", + "operationId": "get-fleet-kubernetes-download", "parameters": [ { "description": "The version of the API to use", @@ -26282,7 +25226,7 @@ "/api/fleet/logstash_api_keys": { "post": { "description": "Generate Logstash API key", - "operationId": "%2Fapi%2Ffleet%2Flogstash_api_keys#0", + "operationId": "post-fleet-logstash-api-keys", "parameters": [ { "description": "The version of the API to use", @@ -26361,7 +25305,7 @@ "/api/fleet/message_signing_service/rotate_key_pair": { "post": { "description": "Rotate fleet message signing key pair", - "operationId": "%2Fapi%2Ffleet%2Fmessage_signing_service%2Frotate_key_pair#0", + "operationId": "post-fleet-message-signing-service-rotate-key-pair", "parameters": [ { "description": "The version of the API to use", @@ -26474,7 +25418,7 @@ "/api/fleet/outputs": { "get": { "description": "List outputs", - "operationId": "%2Fapi%2Ffleet%2Foutputs#0", + "operationId": "get-fleet-outputs", "parameters": [ { "description": "The version of the API to use", @@ -27604,7 +26548,7 @@ }, "post": { "description": "Create output", - "operationId": "%2Fapi%2Ffleet%2Foutputs#1", + "operationId": "post-fleet-outputs", "parameters": [ { "description": "The version of the API to use", @@ -29788,7 +28732,7 @@ "/api/fleet/outputs/{outputId}": { "delete": { "description": "Delete output by ID", - "operationId": "%2Fapi%2Ffleet%2Foutputs%2F%7BoutputId%7D#2", + "operationId": "delete-fleet-outputs-outputid", "parameters": [ { "description": "The version of the API to use", @@ -29898,7 +28842,7 @@ }, "get": { "description": "Get output by ID", - "operationId": "%2Fapi%2Ffleet%2Foutputs%2F%7BoutputId%7D#0", + "operationId": "get-fleet-outputs-outputid", "parameters": [ { "description": "The version of the API to use", @@ -31021,7 +29965,7 @@ }, "put": { "description": "Update output by ID", - "operationId": "%2Fapi%2Ffleet%2Foutputs%2F%7BoutputId%7D#1", + "operationId": "put-fleet-outputs-outputid", "parameters": [ { "description": "The version of the API to use", @@ -33189,7 +32133,7 @@ "/api/fleet/outputs/{outputId}/health": { "get": { "description": "Get latest output health", - "operationId": "%2Fapi%2Ffleet%2Foutputs%2F%7BoutputId%7D%2Fhealth#0", + "operationId": "get-fleet-outputs-outputid-health", "parameters": [ { "description": "The version of the API to use", @@ -33277,7 +32221,7 @@ "/api/fleet/package_policies": { "get": { "description": "List package policies", - "operationId": "%2Fapi%2Ffleet%2Fpackage_policies#0", + "operationId": "get-fleet-package-policies", "parameters": [ { "description": "The version of the API to use", @@ -33992,7 +32936,7 @@ }, "post": { "description": "Create package policy", - "operationId": "%2Fapi%2Ffleet%2Fpackage_policies#1", + "operationId": "post-fleet-package-policies", "parameters": [ { "description": "The version of the API to use", @@ -35266,7 +34210,7 @@ "/api/fleet/package_policies/_bulk_get": { "post": { "description": "Bulk get package policies", - "operationId": "%2Fapi%2Ffleet%2Fpackage_policies%2F_bulk_get#0", + "operationId": "post-fleet-package-policies-bulk-get", "parameters": [ { "description": "The version of the API to use", @@ -35964,7 +34908,7 @@ "/api/fleet/package_policies/delete": { "post": { "description": "Bulk delete package policies", - "operationId": "%2Fapi%2Ffleet%2Fpackage_policies%2Fdelete#0", + "operationId": "post-fleet-package-policies-delete", "parameters": [ { "description": "The version of the API to use", @@ -36168,7 +35112,7 @@ "/api/fleet/package_policies/upgrade": { "post": { "description": "Upgrade package policy to a newer package version", - "operationId": "%2Fapi%2Ffleet%2Fpackage_policies%2Fupgrade#0", + "operationId": "post-fleet-package-policies-upgrade", "parameters": [ { "description": "The version of the API to use", @@ -36293,7 +35237,7 @@ "/api/fleet/package_policies/upgrade/dryrun": { "post": { "description": "Dry run package policy upgrade", - "operationId": "%2Fapi%2Ffleet%2Fpackage_policies%2Fupgrade%2Fdryrun#0", + "operationId": "post-fleet-package-policies-upgrade-dryrun", "parameters": [ { "description": "The version of the API to use", @@ -37479,7 +36423,7 @@ "/api/fleet/package_policies/{packagePolicyId}": { "delete": { "description": "Delete package policy by ID", - "operationId": "%2Fapi%2Ffleet%2Fpackage_policies%2F%7BpackagePolicyId%7D#2", + "operationId": "delete-fleet-package-policies-packagepolicyid", "parameters": [ { "description": "The version of the API to use", @@ -37572,7 +36516,7 @@ }, "get": { "description": "Get package policy by ID", - "operationId": "%2Fapi%2Ffleet%2Fpackage_policies%2F%7BpackagePolicyId%7D#0", + "operationId": "get-fleet-package-policies-packagepolicyid", "parameters": [ { "description": "The version of the API to use", @@ -38238,7 +37182,7 @@ }, "put": { "description": "Update package policy by ID", - "operationId": "%2Fapi%2Ffleet%2Fpackage_policies%2F%7BpackagePolicyId%7D#1", + "operationId": "put-fleet-package-policies-packagepolicyid", "parameters": [ { "description": "The version of the API to use", @@ -39512,7 +38456,7 @@ "/api/fleet/proxies": { "get": { "description": "List proxies", - "operationId": "%2Fapi%2Ffleet%2Fproxies#0", + "operationId": "get-fleet-proxies", "parameters": [ { "description": "The version of the API to use", @@ -39644,7 +38588,7 @@ }, "post": { "description": "Create proxy", - "operationId": "%2Fapi%2Ffleet%2Fproxies#1", + "operationId": "post-fleet-proxies", "parameters": [ { "description": "The version of the API to use", @@ -39831,7 +38775,7 @@ "/api/fleet/proxies/{itemId}": { "delete": { "description": "Delete proxy by ID", - "operationId": "%2Fapi%2Ffleet%2Fproxies%2F%7BitemId%7D#2", + "operationId": "delete-fleet-proxies-itemid", "parameters": [ { "description": "The version of the API to use", @@ -39916,7 +38860,7 @@ }, "get": { "description": "Get proxy by ID", - "operationId": "%2Fapi%2Ffleet%2Fproxies%2F%7BitemId%7D#1", + "operationId": "get-fleet-proxies-itemid", "parameters": [ { "description": "The version of the API to use", @@ -40041,7 +38985,7 @@ }, "put": { "description": "Update proxy by ID", - "operationId": "%2Fapi%2Ffleet%2Fproxies%2F%7BitemId%7D#0", + "operationId": "put-fleet-proxies-itemid", "parameters": [ { "description": "The version of the API to use", @@ -40228,43 +39172,10 @@ ] } }, - "/api/fleet/service-tokens": { - "post": { - "description": "Create a service token", - "operationId": "%2Fapi%2Ffleet%2Fservice-tokens#0", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - }, - { - "description": "A required header to protect against CSRF attacks", - "in": "header", - "name": "kbn-xsrf", - "required": true, - "schema": { - "example": "true", - "type": "string" - } - } - ], - "responses": {}, - "summary": "", - "tags": [] - } - }, "/api/fleet/service_tokens": { "post": { "description": "Create a service token", - "operationId": "%2Fapi%2Ffleet%2Fservice_tokens#0", + "operationId": "post-fleet-service-tokens", "parameters": [ { "description": "The version of the API to use", @@ -40364,7 +39275,7 @@ "/api/fleet/settings": { "get": { "description": "Get settings", - "operationId": "%2Fapi%2Ffleet%2Fsettings#0", + "operationId": "get-fleet-settings", "parameters": [ { "description": "The version of the API to use", @@ -40515,7 +39426,7 @@ }, "put": { "description": "Update settings", - "operationId": "%2Fapi%2Ffleet%2Fsettings#1", + "operationId": "put-fleet-settings", "parameters": [ { "description": "The version of the API to use", @@ -40732,7 +39643,7 @@ "/api/fleet/setup": { "post": { "description": "Initiate Fleet setup", - "operationId": "%2Fapi%2Ffleet%2Fsetup#0", + "operationId": "post-fleet-setup", "parameters": [ { "description": "The version of the API to use", @@ -40851,7 +39762,7 @@ "/api/fleet/uninstall_tokens": { "get": { "description": "List metadata for latest uninstall tokens per agent policy", - "operationId": "%2Fapi%2Ffleet%2Funinstall_tokens#0", + "operationId": "get-fleet-uninstall-tokens", "parameters": [ { "description": "The version of the API to use", @@ -41000,7 +39911,7 @@ "/api/fleet/uninstall_tokens/{uninstallTokenId}": { "get": { "description": "Get one decrypted uninstall token by its ID", - "operationId": "%2Fapi%2Ffleet%2Funinstall_tokens%2F%7BuninstallTokenId%7D#0", + "operationId": "get-fleet-uninstall-tokens-uninstalltokenid", "parameters": [ { "description": "The version of the API to use", @@ -41107,7 +40018,7 @@ }, "/api/security/role": { "get": { - "operationId": "%2Fapi%2Fsecurity%2Frole#0", + "operationId": "get-security-role", "parameters": [ { "description": "The version of the API to use", @@ -41144,7 +40055,7 @@ }, "/api/security/role/{name}": { "delete": { - "operationId": "%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#1", + "operationId": "delete-security-role-name", "parameters": [ { "description": "The version of the API to use", @@ -41189,7 +40100,7 @@ ] }, "get": { - "operationId": "%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#0", + "operationId": "get-security-role-name", "parameters": [ { "description": "The version of the API to use", @@ -41235,7 +40146,7 @@ }, "put": { "description": "Create a new Kibana role or update the attributes of an existing role. Kibana roles are stored in the Elasticsearch native realm.", - "operationId": "%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#2", + "operationId": "put-security-role-name", "parameters": [ { "description": "The version of the API to use", @@ -41554,7 +40465,7 @@ }, "/api/security/roles": { "post": { - "operationId": "%2Fapi%2Fsecurity%2Froles#0", + "operationId": "post-security-roles", "parameters": [ { "description": "The version of the API to use", @@ -41864,8 +40775,8 @@ }, "/api/spaces/_copy_saved_objects": { "post": { - "description": "It also allows you to automatically copy related objects, so when you copy a dashboard, this can automatically copy over the associated visualizations, data views, and saved searches, as required. You can request to overwrite any objects that already exist in the target space if they share an identifier or you can use the resolve copy saved objects conflicts API to do this on a per-object basis.", - "operationId": "%2Fapi%2Fspaces%2F_copy_saved_objects#0", + "description": "It also allows you to automatically copy related objects, so when you copy a dashboard, this can automatically copy over the associated visualizations, data views, and saved searches, as required. You can request to overwrite any objects that already exist in the target space if they share an identifier or you can use the resolve copy saved objects conflicts API to do this on a per-object basis.

[Required authorization] Route required privileges: ALL of [copySavedObjectsToSpaces].", + "operationId": "post-spaces-copy-saved-objects", "parameters": [ { "description": "The version of the API to use", @@ -41963,7 +40874,7 @@ }, "/api/spaces/_disable_legacy_url_aliases": { "post": { - "operationId": "%2Fapi%2Fspaces%2F_disable_legacy_url_aliases#0", + "operationId": "post-spaces-disable-legacy-url-aliases", "parameters": [ { "description": "The version of the API to use", @@ -42039,7 +40950,7 @@ "/api/spaces/_get_shareable_references": { "post": { "description": "Collect references and space contexts for saved objects.", - "operationId": "%2Fapi%2Fspaces%2F_get_shareable_references#0", + "operationId": "post-spaces-get-shareable-references", "parameters": [ { "description": "The version of the API to use", @@ -42107,8 +41018,8 @@ }, "/api/spaces/_resolve_copy_saved_objects_errors": { "post": { - "description": "Overwrite saved objects that are returned as errors from the copy saved objects to space API.", - "operationId": "%2Fapi%2Fspaces%2F_resolve_copy_saved_objects_errors#0", + "description": "Overwrite saved objects that are returned as errors from the copy saved objects to space API.

[Required authorization] Route required privileges: ALL of [copySavedObjectsToSpaces].", + "operationId": "post-spaces-resolve-copy-saved-objects-errors", "parameters": [ { "description": "The version of the API to use", @@ -42229,7 +41140,7 @@ "/api/spaces/_update_objects_spaces": { "post": { "description": "Update one or more saved objects to add or remove them from some spaces.", - "operationId": "%2Fapi%2Fspaces%2F_update_objects_spaces#0", + "operationId": "post-spaces-update-objects-spaces", "parameters": [ { "description": "The version of the API to use", @@ -42315,7 +41226,7 @@ }, "/api/spaces/space": { "get": { - "operationId": "%2Fapi%2Fspaces%2Fspace#0", + "operationId": "get-spaces-space", "parameters": [ { "description": "The version of the API to use", @@ -42395,7 +41306,7 @@ ] }, "post": { - "operationId": "%2Fapi%2Fspaces%2Fspace#1", + "operationId": "post-spaces-space", "parameters": [ { "description": "The version of the API to use", @@ -42496,7 +41407,7 @@ "/api/spaces/space/{id}": { "delete": { "description": "When you delete a space, all saved objects that belong to the space are automatically deleted, which is permanent and cannot be undone.", - "operationId": "%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#2", + "operationId": "delete-spaces-space-id", "parameters": [ { "description": "The version of the API to use", @@ -42544,7 +41455,7 @@ ] }, "get": { - "operationId": "%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#0", + "operationId": "get-spaces-space-id", "parameters": [ { "description": "The version of the API to use", @@ -42579,7 +41490,7 @@ ] }, "put": { - "operationId": "%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#1", + "operationId": "put-spaces-space-id", "parameters": [ { "description": "The version of the API to use", @@ -42688,7 +41599,7 @@ }, "/api/status": { "get": { - "operationId": "%2Fapi%2Fstatus#0", + "operationId": "get-status", "parameters": [ { "description": "The version of the API to use", diff --git a/oas_docs/bundle.serverless.json b/oas_docs/bundle.serverless.json index 3e1116bc1bcc9..f171dadde991a 100644 --- a/oas_docs/bundle.serverless.json +++ b/oas_docs/bundle.serverless.json @@ -344,10 +344,10 @@ }, "openapi": "3.0.0", "paths": { - "/api/actions": { - "get": { - "deprecated": true, - "operationId": "%2Fapi%2Factions#0", + "/api/actions/connector/{id}": { + "delete": { + "description": "WARNING: When you delete a connector, it cannot be recovered.", + "operationId": "delete-actions-connector-id", "parameters": [ { "description": "The version of the API to use", @@ -360,19 +360,39 @@ ], "type": "string" } + }, + { + "description": "A required header to protect against CSRF attacks", + "in": "header", + "name": "kbn-xsrf", + "required": true, + "schema": { + "example": "true", + "type": "string" + } + }, + { + "description": "An identifier for the connector.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } } ], - "responses": {}, - "summary": "Get all connectors", + "responses": { + "204": { + "description": "Indicates a successful call." + } + }, + "summary": "Delete a connector", "tags": [ "connectors" ] - } - }, - "/api/actions/action": { - "post": { - "deprecated": true, - "operationId": "%2Fapi%2Factions%2Faction#0", + }, + "get": { + "operationId": "get-actions-connector-id", "parameters": [ { "description": "The version of the API to use", @@ -387,50 +407,15 @@ } }, { - "description": "A required header to protect against CSRF attacks", - "in": "header", - "name": "kbn-xsrf", + "description": "An identifier for the connector.", + "in": "path", + "name": "id", "required": true, "schema": { - "example": "true", "type": "string" } } ], - "requestBody": { - "content": { - "application/json; Elastic-Api-Version=2023-10-31": { - "schema": { - "additionalProperties": false, - "properties": { - "actionTypeId": { - "description": "The connector type identifier.", - "type": "string" - }, - "config": { - "additionalProperties": {}, - "default": {}, - "type": "object" - }, - "name": { - "description": "The display name for the connector.", - "type": "string" - }, - "secrets": { - "additionalProperties": {}, - "default": {}, - "type": "object" - } - }, - "required": [ - "name", - "actionTypeId" - ], - "type": "object" - } - } - } - }, "responses": { "200": { "content": { @@ -486,17 +471,13 @@ "description": "Indicates a successful call." } }, - "summary": "Create a connector", + "summary": "Get connector information", "tags": [ "connectors" ] - } - }, - "/api/actions/action/{id}": { - "delete": { - "deprecated": true, - "description": "WARNING: When you delete a connector, it cannot be recovered.", - "operationId": "%2Fapi%2Factions%2Faction%2F%7Bid%7D#0", + }, + "post": { + "operationId": "post-actions-connector-id", "parameters": [ { "description": "The version of the API to use", @@ -524,48 +505,46 @@ "description": "An identifier for the connector.", "in": "path", "name": "id", - "required": true, + "required": false, "schema": { "type": "string" } } ], - "responses": { - "204": { - "description": "Indicates a successful call." - } - }, - "summary": "Delete a connector", - "tags": [ - "connectors" - ] - }, - "get": { - "deprecated": true, - "operationId": "%2Fapi%2Factions%2Faction%2F%7Bid%7D#1", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - }, - { - "description": "An identifier for the connector.", - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" + "requestBody": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "properties": { + "config": { + "additionalProperties": {}, + "default": {}, + "type": "object" + }, + "connector_type_id": { + "description": "The type of connector.", + "type": "string" + }, + "name": { + "description": "The display name for the connector.", + "type": "string" + }, + "secrets": { + "additionalProperties": {}, + "default": {}, + "type": "object" + } + }, + "required": [ + "name", + "connector_type_id" + ], + "type": "object" + } } } - ], + }, "responses": { "200": { "content": { @@ -621,14 +600,13 @@ "description": "Indicates a successful call." } }, - "summary": "Get connector information", + "summary": "Create a connector", "tags": [ "connectors" ] }, "put": { - "deprecated": true, - "operationId": "%2Fapi%2Factions%2Faction%2F%7Bid%7D#2", + "operationId": "put-actions-connector-id", "parameters": [ { "description": "The version of the API to use", @@ -674,6 +652,7 @@ "type": "object" }, "name": { + "description": "The display name for the connector.", "type": "string" }, "secrets": { @@ -751,10 +730,10 @@ ] } }, - "/api/actions/action/{id}/_execute": { + "/api/actions/connector/{id}/_execute": { "post": { - "deprecated": true, - "operationId": "%2Fapi%2Factions%2Faction%2F%7Bid%7D%2F_execute#0", + "description": "You can use this API to test an action that involves interaction with Kibana services or integrations with third-party systems.", + "operationId": "post-actions-connector-id-execute", "parameters": [ { "description": "The version of the API to use", @@ -868,10 +847,10 @@ ] } }, - "/api/actions/connector/{id}": { - "delete": { - "description": "WARNING: When you delete a connector, it cannot be recovered.", - "operationId": "%2Fapi%2Factions%2Fconnector%2F%7Bid%7D#0", + "/api/actions/connector_types": { + "get": { + "description": "You do not need any Kibana feature privileges to run this API.", + "operationId": "get-actions-connector-types", "parameters": [ { "description": "The version of the API to use", @@ -886,37 +865,25 @@ } }, { - "description": "A required header to protect against CSRF attacks", - "in": "header", - "name": "kbn-xsrf", - "required": true, - "schema": { - "example": "true", - "type": "string" - } - }, - { - "description": "An identifier for the connector.", - "in": "path", - "name": "id", - "required": true, + "description": "A filter to limit the retrieved connector types to those that support a specific feature (such as alerting or cases).", + "in": "query", + "name": "feature_id", + "required": false, "schema": { "type": "string" } } ], - "responses": { - "204": { - "description": "Indicates a successful call." - } - }, - "summary": "Delete a connector", + "responses": {}, + "summary": "Get connector types", "tags": [ "connectors" ] - }, + } + }, + "/api/actions/connectors": { "get": { - "operationId": "%2Fapi%2Factions%2Fconnector%2F%7Bid%7D#1", + "operationId": "get-actions-connectors", "parameters": [ { "description": "The version of the API to use", @@ -929,534 +896,18 @@ ], "type": "string" } - }, - { - "description": "An identifier for the connector.", - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } } ], - "responses": { - "200": { - "content": { - "application/json; Elastic-Api-Version=2023-10-31": { - "schema": { - "additionalProperties": false, - "properties": { - "config": { - "additionalProperties": {}, - "type": "object" - }, - "connector_type_id": { - "description": "The connector type identifier.", - "type": "string" - }, - "id": { - "description": "The identifier for the connector.", - "type": "string" - }, - "is_deprecated": { - "description": "Indicates whether the connector is deprecated.", - "type": "boolean" - }, - "is_missing_secrets": { - "description": "Indicates whether the connector is missing secrets.", - "type": "boolean" - }, - "is_preconfigured": { - "description": "Indicates whether the connector is preconfigured. If true, the `config` and `is_missing_secrets` properties are omitted from the response. ", - "type": "boolean" - }, - "is_system_action": { - "description": "Indicates whether the connector is used for system actions.", - "type": "boolean" - }, - "name": { - "description": " The name of the rule.", - "type": "string" - } - }, - "required": [ - "id", - "name", - "connector_type_id", - "is_preconfigured", - "is_deprecated", - "is_system_action" - ], - "type": "object" - } - } - }, - "description": "Indicates a successful call." - } - }, - "summary": "Get connector information", + "responses": {}, + "summary": "Get all connectors", "tags": [ "connectors" ] - }, - "post": { - "operationId": "%2Fapi%2Factions%2Fconnector%2F%7Bid%3F%7D#0", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - }, - { - "description": "A required header to protect against CSRF attacks", - "in": "header", - "name": "kbn-xsrf", - "required": true, - "schema": { - "example": "true", - "type": "string" - } - }, - { - "description": "An identifier for the connector.", - "in": "path", - "name": "id", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json; Elastic-Api-Version=2023-10-31": { - "schema": { - "additionalProperties": false, - "properties": { - "config": { - "additionalProperties": {}, - "default": {}, - "type": "object" - }, - "connector_type_id": { - "description": "The type of connector.", - "type": "string" - }, - "name": { - "description": "The display name for the connector.", - "type": "string" - }, - "secrets": { - "additionalProperties": {}, - "default": {}, - "type": "object" - } - }, - "required": [ - "name", - "connector_type_id" - ], - "type": "object" - } - } - } - }, - "responses": { - "200": { - "content": { - "application/json; Elastic-Api-Version=2023-10-31": { - "schema": { - "additionalProperties": false, - "properties": { - "config": { - "additionalProperties": {}, - "type": "object" - }, - "connector_type_id": { - "description": "The connector type identifier.", - "type": "string" - }, - "id": { - "description": "The identifier for the connector.", - "type": "string" - }, - "is_deprecated": { - "description": "Indicates whether the connector is deprecated.", - "type": "boolean" - }, - "is_missing_secrets": { - "description": "Indicates whether the connector is missing secrets.", - "type": "boolean" - }, - "is_preconfigured": { - "description": "Indicates whether the connector is preconfigured. If true, the `config` and `is_missing_secrets` properties are omitted from the response. ", - "type": "boolean" - }, - "is_system_action": { - "description": "Indicates whether the connector is used for system actions.", - "type": "boolean" - }, - "name": { - "description": " The name of the rule.", - "type": "string" - } - }, - "required": [ - "id", - "name", - "connector_type_id", - "is_preconfigured", - "is_deprecated", - "is_system_action" - ], - "type": "object" - } - } - }, - "description": "Indicates a successful call." - } - }, - "summary": "Create a connector", - "tags": [ - "connectors" - ] - }, - "put": { - "operationId": "%2Fapi%2Factions%2Fconnector%2F%7Bid%7D#2", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - }, - { - "description": "A required header to protect against CSRF attacks", - "in": "header", - "name": "kbn-xsrf", - "required": true, - "schema": { - "example": "true", - "type": "string" - } - }, - { - "description": "An identifier for the connector.", - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json; Elastic-Api-Version=2023-10-31": { - "schema": { - "additionalProperties": false, - "properties": { - "config": { - "additionalProperties": {}, - "default": {}, - "type": "object" - }, - "name": { - "description": "The display name for the connector.", - "type": "string" - }, - "secrets": { - "additionalProperties": {}, - "default": {}, - "type": "object" - } - }, - "required": [ - "name" - ], - "type": "object" - } - } - } - }, - "responses": { - "200": { - "content": { - "application/json; Elastic-Api-Version=2023-10-31": { - "schema": { - "additionalProperties": false, - "properties": { - "config": { - "additionalProperties": {}, - "type": "object" - }, - "connector_type_id": { - "description": "The connector type identifier.", - "type": "string" - }, - "id": { - "description": "The identifier for the connector.", - "type": "string" - }, - "is_deprecated": { - "description": "Indicates whether the connector is deprecated.", - "type": "boolean" - }, - "is_missing_secrets": { - "description": "Indicates whether the connector is missing secrets.", - "type": "boolean" - }, - "is_preconfigured": { - "description": "Indicates whether the connector is preconfigured. If true, the `config` and `is_missing_secrets` properties are omitted from the response. ", - "type": "boolean" - }, - "is_system_action": { - "description": "Indicates whether the connector is used for system actions.", - "type": "boolean" - }, - "name": { - "description": " The name of the rule.", - "type": "string" - } - }, - "required": [ - "id", - "name", - "connector_type_id", - "is_preconfigured", - "is_deprecated", - "is_system_action" - ], - "type": "object" - } - } - }, - "description": "Indicates a successful call." - } - }, - "summary": "Update a connector", - "tags": [ - "connectors" - ] - } - }, - "/api/actions/connector/{id}/_execute": { - "post": { - "description": "You can use this API to test an action that involves interaction with Kibana services or integrations with third-party systems.", - "operationId": "%2Fapi%2Factions%2Fconnector%2F%7Bid%7D%2F_execute#0", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - }, - { - "description": "A required header to protect against CSRF attacks", - "in": "header", - "name": "kbn-xsrf", - "required": true, - "schema": { - "example": "true", - "type": "string" - } - }, - { - "description": "An identifier for the connector.", - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json; Elastic-Api-Version=2023-10-31": { - "schema": { - "additionalProperties": false, - "properties": { - "params": { - "additionalProperties": {}, - "type": "object" - } - }, - "required": [ - "params" - ], - "type": "object" - } - } - } - }, - "responses": { - "200": { - "content": { - "application/json; Elastic-Api-Version=2023-10-31": { - "schema": { - "additionalProperties": false, - "properties": { - "config": { - "additionalProperties": {}, - "type": "object" - }, - "connector_type_id": { - "description": "The connector type identifier.", - "type": "string" - }, - "id": { - "description": "The identifier for the connector.", - "type": "string" - }, - "is_deprecated": { - "description": "Indicates whether the connector is deprecated.", - "type": "boolean" - }, - "is_missing_secrets": { - "description": "Indicates whether the connector is missing secrets.", - "type": "boolean" - }, - "is_preconfigured": { - "description": "Indicates whether the connector is preconfigured. If true, the `config` and `is_missing_secrets` properties are omitted from the response. ", - "type": "boolean" - }, - "is_system_action": { - "description": "Indicates whether the connector is used for system actions.", - "type": "boolean" - }, - "name": { - "description": " The name of the rule.", - "type": "string" - } - }, - "required": [ - "id", - "name", - "connector_type_id", - "is_preconfigured", - "is_deprecated", - "is_system_action" - ], - "type": "object" - } - } - }, - "description": "Indicates a successful call." - } - }, - "summary": "Run a connector", - "tags": [ - "connectors" - ] - } - }, - "/api/actions/connector_types": { - "get": { - "description": "You do not need any Kibana feature privileges to run this API.", - "operationId": "%2Fapi%2Factions%2Fconnector_types#0", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - }, - { - "description": "A filter to limit the retrieved connector types to those that support a specific feature (such as alerting or cases).", - "in": "query", - "name": "feature_id", - "required": false, - "schema": { - "type": "string" - } - } - ], - "responses": {}, - "summary": "Get connector types", - "tags": [ - "connectors" - ] - } - }, - "/api/actions/connectors": { - "get": { - "operationId": "%2Fapi%2Factions%2Fconnectors#0", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - } - ], - "responses": {}, - "summary": "Get all connectors", - "tags": [ - "connectors" - ] - } - }, - "/api/actions/list_action_types": { - "get": { - "deprecated": true, - "operationId": "%2Fapi%2Factions%2Flist_action_types#0", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - } - ], - "responses": {}, - "summary": "Get connector types", - "tags": [ - "connectors" - ] - } - }, - "/api/alerting/rule/{id}": { - "delete": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Bid%7D#2", + } + }, + "/api/alerting/rule/{id}": { + "delete": { + "operationId": "delete-alerting-rule-id", "parameters": [ { "description": "The version of the API to use", @@ -1510,7 +961,7 @@ ] }, "get": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Bid%7D#0", + "operationId": "get-alerting-rule-id", "parameters": [ { "description": "The version of the API to use", @@ -2388,7 +1839,7 @@ ] }, "post": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Bid%3F%7D#0", + "operationId": "post-alerting-rule-id", "parameters": [ { "description": "The version of the API to use", @@ -3568,7 +3019,7 @@ ] }, "put": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Bid%7D#1", + "operationId": "put-alerting-rule-id", "parameters": [ { "description": "The version of the API to use", @@ -4736,7 +4187,7 @@ }, "/api/alerting/rule/{id}/_disable": { "post": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_disable#0", + "operationId": "post-alerting-rule-id-disable", "parameters": [ { "description": "The version of the API to use", @@ -4810,7 +4261,7 @@ }, "/api/alerting/rule/{id}/_enable": { "post": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_enable#0", + "operationId": "post-alerting-rule-id-enable", "parameters": [ { "description": "The version of the API to use", @@ -4866,7 +4317,7 @@ }, "/api/alerting/rule/{id}/_mute_all": { "post": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_mute_all#0", + "operationId": "post-alerting-rule-id-mute-all", "parameters": [ { "description": "The version of the API to use", @@ -4922,7 +4373,7 @@ }, "/api/alerting/rule/{id}/_unmute_all": { "post": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_unmute_all#0", + "operationId": "post-alerting-rule-id-unmute-all", "parameters": [ { "description": "The version of the API to use", @@ -4978,7 +4429,7 @@ }, "/api/alerting/rule/{id}/_update_api_key": { "post": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_update_api_key#0", + "operationId": "post-alerting-rule-id-update-api-key", "parameters": [ { "description": "The version of the API to use", @@ -5037,7 +4488,7 @@ }, "/api/alerting/rule/{rule_id}/alert/{alert_id}/_mute": { "post": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Brule_id%7D%2Falert%2F%7Balert_id%7D%2F_mute#0", + "operationId": "post-alerting-rule-rule-id-alert-alert-id-mute", "parameters": [ { "description": "The version of the API to use", @@ -5102,7 +4553,7 @@ }, "/api/alerting/rule/{rule_id}/alert/{alert_id}/_unmute": { "post": { - "operationId": "%2Fapi%2Falerting%2Frule%2F%7Brule_id%7D%2Falert%2F%7Balert_id%7D%2F_unmute#0", + "operationId": "post-alerting-rule-rule-id-alert-alert-id-unmute", "parameters": [ { "description": "The version of the API to use", @@ -5167,7 +4618,7 @@ }, "/api/alerting/rules/_find": { "get": { - "operationId": "%2Fapi%2Falerting%2Frules%2F_find#0", + "operationId": "get-alerting-rules-find", "parameters": [ { "description": "The version of the API to use", @@ -6175,67 +5626,10 @@ ] } }, - "/api/fleet/agent-status": { - "get": { - "operationId": "%2Fapi%2Ffleet%2Fagent-status#0", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - }, - { - "in": "query", - "name": "policyId", - "required": false, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "policyIds", - "required": false, - "schema": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ] - } - }, - { - "in": "query", - "name": "kuery", - "required": false, - "schema": { - "deprecated": true, - "type": "string" - } - } - ], - "responses": {}, - "summary": "", - "tags": [] - } - }, "/api/fleet/agent_download_sources": { "get": { "description": "List agent binary download sources", - "operationId": "%2Fapi%2Ffleet%2Fagent_download_sources#0", + "operationId": "get-fleet-agent-download-sources", "parameters": [ { "description": "The version of the API to use", @@ -6344,7 +5738,7 @@ }, "post": { "description": "Create agent binary download source", - "operationId": "%2Fapi%2Ffleet%2Fagent_download_sources#1", + "operationId": "post-fleet-agent-download-sources", "parameters": [ { "description": "The version of the API to use", @@ -6485,7 +5879,7 @@ "/api/fleet/agent_download_sources/{sourceId}": { "delete": { "description": "Delete agent binary download source by ID", - "operationId": "%2Fapi%2Ffleet%2Fagent_download_sources%2F%7BsourceId%7D#2", + "operationId": "delete-fleet-agent-download-sources-sourceid", "parameters": [ { "description": "The version of the API to use", @@ -6570,7 +5964,7 @@ }, "get": { "description": "Get agent binary download source by ID", - "operationId": "%2Fapi%2Ffleet%2Fagent_download_sources%2F%7BsourceId%7D#0", + "operationId": "get-fleet-agent-download-sources-sourceid", "parameters": [ { "description": "The version of the API to use", @@ -6672,7 +6066,7 @@ }, "put": { "description": "Update agent binary download source by ID", - "operationId": "%2Fapi%2Ffleet%2Fagent_download_sources%2F%7BsourceId%7D#1", + "operationId": "put-fleet-agent-download-sources-sourceid", "parameters": [ { "description": "The version of the API to use", @@ -6821,7 +6215,7 @@ "/api/fleet/agent_policies": { "get": { "description": "List agent policies", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies#0", + "operationId": "get-fleet-agent-policies", "parameters": [ { "description": "The version of the API to use", @@ -7659,7 +7053,7 @@ }, "post": { "description": "Create an agent policy", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies#1", + "operationId": "post-fleet-agent-policies", "parameters": [ { "description": "The version of the API to use", @@ -8654,7 +8048,7 @@ "/api/fleet/agent_policies/_bulk_get": { "post": { "description": "Bulk get agent policies", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2F_bulk_get#0", + "operationId": "post-fleet-agent-policies-bulk-get", "parameters": [ { "description": "The version of the API to use", @@ -9441,7 +8835,7 @@ "/api/fleet/agent_policies/delete": { "post": { "description": "Delete agent policy by ID", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2Fdelete#0", + "operationId": "post-fleet-agent-policies-delete", "parameters": [ { "description": "The version of the API to use", @@ -9546,7 +8940,7 @@ "/api/fleet/agent_policies/outputs": { "post": { "description": "Get list of outputs associated with agent policies", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2Foutputs#0", + "operationId": "post-fleet-agent-policies-outputs", "parameters": [ { "description": "The version of the API to use", @@ -9731,7 +9125,7 @@ "/api/fleet/agent_policies/{agentPolicyId}": { "get": { "description": "Get an agent policy by ID", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D#0", + "operationId": "get-fleet-agent-policies-agentpolicyid", "parameters": [ { "description": "The version of the API to use", @@ -10482,7 +9876,7 @@ }, "put": { "description": "Update an agent policy by ID", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D#1", + "operationId": "put-fleet-agent-policies-agentpolicyid", "parameters": [ { "description": "The version of the API to use", @@ -11489,7 +10883,7 @@ "/api/fleet/agent_policies/{agentPolicyId}/copy": { "post": { "description": "Copy an agent policy by ID", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Fcopy#0", + "operationId": "post-fleet-agent-policies-agentpolicyid-copy", "parameters": [ { "description": "The version of the API to use", @@ -12274,7 +11668,7 @@ "/api/fleet/agent_policies/{agentPolicyId}/download": { "get": { "description": "Download an agent policy by ID", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Fdownload#0", + "operationId": "get-fleet-agent-policies-agentpolicyid-download", "parameters": [ { "description": "The version of the API to use", @@ -12391,7 +11785,7 @@ "/api/fleet/agent_policies/{agentPolicyId}/full": { "get": { "description": "Get a full agent policy by ID", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Ffull#0", + "operationId": "get-fleet-agent-policies-agentpolicyid-full", "parameters": [ { "description": "The version of the API to use", @@ -12893,7 +12287,7 @@ "/api/fleet/agent_policies/{agentPolicyId}/outputs": { "get": { "description": "Get list of outputs associated with agent policy by policy id", - "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Foutputs#0", + "operationId": "get-fleet-agent-policies-agentpolicyid-outputs", "parameters": [ { "description": "The version of the API to use", @@ -13051,7 +12445,7 @@ "/api/fleet/agent_status": { "get": { "description": "Get agent status summary", - "operationId": "%2Fapi%2Ffleet%2Fagent_status#0", + "operationId": "get-fleet-agent-status", "parameters": [ { "description": "The version of the API to use", @@ -13096,7 +12490,6 @@ "name": "kuery", "required": false, "schema": { - "deprecated": true, "type": "string" } } @@ -13135,10 +12528,6 @@ "other": { "type": "number" }, - "total": { - "deprecated": true, - "type": "number" - }, "unenrolled": { "type": "number" }, @@ -13148,7 +12537,6 @@ }, "required": [ "events", - "total", "online", "error", "offline", @@ -13205,7 +12593,7 @@ "/api/fleet/agent_status/data": { "get": { "description": "Get incoming agent data", - "operationId": "%2Fapi%2Ffleet%2Fagent_status%2Fdata#0", + "operationId": "get-fleet-agent-status-data", "parameters": [ { "description": "The version of the API to use", @@ -13321,7 +12709,7 @@ "/api/fleet/agents": { "get": { "description": "List agents", - "operationId": "%2Fapi%2Ffleet%2Fagents#0", + "operationId": "get-fleet-agents", "parameters": [ { "description": "The version of the API to use", @@ -13812,394 +13200,6 @@ }, "type": "array" }, - "list": { - "deprecated": true, - "items": { - "additionalProperties": false, - "properties": { - "access_api_key": { - "type": "string" - }, - "access_api_key_id": { - "type": "string" - }, - "active": { - "type": "boolean" - }, - "agent": { - "additionalProperties": true, - "properties": { - "id": { - "type": "string" - }, - "version": { - "type": "string" - } - }, - "required": [ - "id", - "version" - ], - "type": "object" - }, - "components": { - "items": { - "additionalProperties": false, - "properties": { - "id": { - "type": "string" - }, - "message": { - "type": "string" - }, - "status": { - "enum": [ - "STARTING", - "CONFIGURING", - "HEALTHY", - "DEGRADED", - "FAILED", - "STOPPING", - "STOPPED" - ], - "type": "string" - }, - "type": { - "type": "string" - }, - "units": { - "items": { - "additionalProperties": false, - "properties": { - "id": { - "type": "string" - }, - "message": { - "type": "string" - }, - "payload": { - "additionalProperties": {}, - "type": "object" - }, - "status": { - "enum": [ - "STARTING", - "CONFIGURING", - "HEALTHY", - "DEGRADED", - "FAILED", - "STOPPING", - "STOPPED" - ], - "type": "string" - }, - "type": { - "enum": [ - "input", - "output" - ], - "type": "string" - } - }, - "required": [ - "id", - "type", - "status", - "message" - ], - "type": "object" - }, - "type": "array" - } - }, - "required": [ - "id", - "type", - "status", - "message" - ], - "type": "object" - }, - "type": "array" - }, - "default_api_key": { - "type": "string" - }, - "default_api_key_history": { - "items": { - "additionalProperties": false, - "deprecated": true, - "properties": { - "id": { - "type": "string" - }, - "retired_at": { - "type": "string" - } - }, - "required": [ - "id", - "retired_at" - ], - "type": "object" - }, - "type": "array" - }, - "default_api_key_id": { - "type": "string" - }, - "enrolled_at": { - "type": "string" - }, - "id": { - "type": "string" - }, - "last_checkin": { - "type": "string" - }, - "last_checkin_message": { - "type": "string" - }, - "last_checkin_status": { - "enum": [ - "error", - "online", - "degraded", - "updating", - "starting" - ], - "type": "string" - }, - "local_metadata": { - "additionalProperties": {}, - "type": "object" - }, - "metrics": { - "additionalProperties": false, - "properties": { - "cpu_avg": { - "type": "number" - }, - "memory_size_byte_avg": { - "type": "number" - } - }, - "type": "object" - }, - "namespaces": { - "items": { - "type": "string" - }, - "type": "array" - }, - "outputs": { - "additionalProperties": { - "additionalProperties": false, - "properties": { - "api_key_id": { - "type": "string" - }, - "to_retire_api_key_ids": { - "items": { - "additionalProperties": false, - "properties": { - "id": { - "type": "string" - }, - "retired_at": { - "type": "string" - } - }, - "required": [ - "id", - "retired_at" - ], - "type": "object" - }, - "type": "array" - }, - "type": { - "type": "string" - } - }, - "required": [ - "api_key_id", - "type" - ], - "type": "object" - }, - "type": "object" - }, - "packages": { - "items": { - "type": "string" - }, - "type": "array" - }, - "policy_id": { - "type": "string" - }, - "policy_revision": { - "nullable": true, - "type": "number" - }, - "sort": { - "items": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "string" - }, - { - "enum": [], - "nullable": true - } - ] - }, - "type": "array" - }, - "status": { - "enum": [ - "offline", - "error", - "online", - "inactive", - "enrolling", - "unenrolling", - "unenrolled", - "updating", - "degraded" - ], - "type": "string" - }, - "tags": { - "items": { - "type": "string" - }, - "type": "array" - }, - "type": { - "enum": [ - "PERMANENT", - "EPHEMERAL", - "TEMPORARY" - ], - "type": "string" - }, - "unenrolled_at": { - "type": "string" - }, - "unenrollment_started_at": { - "type": "string" - }, - "unhealthy_reason": { - "items": { - "enum": [ - "input", - "output", - "other" - ], - "type": "string" - }, - "nullable": true, - "type": "array" - }, - "upgrade_details": { - "additionalProperties": false, - "properties": { - "action_id": { - "type": "string" - }, - "metadata": { - "additionalProperties": false, - "properties": { - "download_percent": { - "type": "number" - }, - "download_rate": { - "type": "number" - }, - "error_msg": { - "type": "string" - }, - "failed_state": { - "enum": [ - "UPG_REQUESTED", - "UPG_SCHEDULED", - "UPG_DOWNLOADING", - "UPG_EXTRACTING", - "UPG_REPLACING", - "UPG_RESTARTING", - "UPG_FAILED", - "UPG_WATCHING", - "UPG_ROLLBACK" - ], - "type": "string" - }, - "retry_error_msg": { - "type": "string" - }, - "retry_until": { - "type": "string" - }, - "scheduled_at": { - "type": "string" - } - }, - "type": "object" - }, - "state": { - "enum": [ - "UPG_REQUESTED", - "UPG_SCHEDULED", - "UPG_DOWNLOADING", - "UPG_EXTRACTING", - "UPG_REPLACING", - "UPG_RESTARTING", - "UPG_FAILED", - "UPG_WATCHING", - "UPG_ROLLBACK" - ], - "type": "string" - }, - "target_version": { - "type": "string" - } - }, - "required": [ - "target_version", - "action_id", - "state" - ], - "type": "object" - }, - "upgrade_started_at": { - "nullable": true, - "type": "string" - }, - "upgraded_at": { - "nullable": true, - "type": "string" - }, - "user_provided_metadata": { - "additionalProperties": {}, - "type": "object" - } - }, - "required": [ - "id", - "packages", - "type", - "active", - "enrolled_at", - "local_metadata" - ], - "type": "object" - }, - "type": "array" - }, "page": { "type": "number" }, @@ -14260,7 +13260,7 @@ }, "post": { "description": "List agents by action ids", - "operationId": "%2Fapi%2Ffleet%2Fagents#1", + "operationId": "post-fleet-agents", "parameters": [ { "description": "The version of the API to use", @@ -14363,7 +13363,7 @@ "/api/fleet/agents/action_status": { "get": { "description": "Get agent action status", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Faction_status#0", + "operationId": "get-fleet-agents-action-status", "parameters": [ { "description": "The version of the API to use", @@ -14599,7 +13599,7 @@ "/api/fleet/agents/actions/{actionId}/cancel": { "post": { "description": "Cancel agent action", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Factions%2F%7BactionId%7D%2Fcancel#0", + "operationId": "post-fleet-agents-actions-actionid-cancel", "parameters": [ { "description": "The version of the API to use", @@ -14741,7 +13741,7 @@ "/api/fleet/agents/available_versions": { "get": { "description": "Get available agent versions", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Favailable_versions#0", + "operationId": "get-fleet-agents-available-versions", "parameters": [ { "description": "The version of the API to use", @@ -14813,7 +13813,7 @@ "/api/fleet/agents/bulk_reassign": { "post": { "description": "Bulk reassign agents", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Fbulk_reassign#0", + "operationId": "post-fleet-agents-bulk-reassign", "parameters": [ { "description": "The version of the API to use", @@ -14931,7 +13931,7 @@ "/api/fleet/agents/bulk_request_diagnostics": { "post": { "description": "Bulk request diagnostics from agents", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Fbulk_request_diagnostics#0", + "operationId": "post-fleet-agents-bulk-request-diagnostics", "parameters": [ { "description": "The version of the API to use", @@ -15050,7 +14050,7 @@ "/api/fleet/agents/bulk_unenroll": { "post": { "description": "Bulk unenroll agents", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Fbulk_unenroll#0", + "operationId": "post-fleet-agents-bulk-unenroll", "parameters": [ { "description": "The version of the API to use", @@ -15174,7 +14174,7 @@ "/api/fleet/agents/bulk_update_agent_tags": { "post": { "description": "Bulk update agent tags", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Fbulk_update_agent_tags#0", + "operationId": "post-fleet-agents-bulk-update-agent-tags", "parameters": [ { "description": "The version of the API to use", @@ -15300,7 +14300,7 @@ "/api/fleet/agents/bulk_upgrade": { "post": { "description": "Bulk upgrade agents", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Fbulk_upgrade#0", + "operationId": "post-fleet-agents-bulk-upgrade", "parameters": [ { "description": "The version of the API to use", @@ -15434,7 +14434,7 @@ "/api/fleet/agents/files/{fileId}": { "delete": { "description": "Delete file uploaded by agent", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Ffiles%2F%7BfileId%7D#0", + "operationId": "delete-fleet-agents-files-fileid", "parameters": [ { "description": "The version of the API to use", @@ -15525,7 +14525,7 @@ "/api/fleet/agents/files/{fileId}/{fileName}": { "get": { "description": "Get file uploaded by agent", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Ffiles%2F%7BfileId%7D%2F%7BfileName%7D#0", + "operationId": "get-fleet-agents-files-fileid-filename", "parameters": [ { "description": "The version of the API to use", @@ -15601,7 +14601,7 @@ "/api/fleet/agents/setup": { "get": { "description": "Get agent setup info", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Fsetup#0", + "operationId": "get-fleet-agents-setup", "parameters": [ { "description": "The version of the API to use", @@ -15702,7 +14702,7 @@ }, "post": { "description": "Initiate agent setup", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Fsetup#1", + "operationId": "post-fleet-agents-setup", "parameters": [ { "description": "The version of the API to use", @@ -15802,7 +14802,7 @@ "/api/fleet/agents/tags": { "get": { "description": "List agent tags", - "operationId": "%2Fapi%2Ffleet%2Fagents%2Ftags#0", + "operationId": "get-fleet-agents-tags", "parameters": [ { "description": "The version of the API to use", @@ -15891,7 +14891,7 @@ "/api/fleet/agents/{agentId}": { "delete": { "description": "Delete agent by ID", - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D#2", + "operationId": "delete-fleet-agents-agentid", "parameters": [ { "description": "The version of the API to use", @@ -15979,7 +14979,7 @@ }, "get": { "description": "Get agent by ID", - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D#0", + "operationId": "get-fleet-agents-agentid", "parameters": [ { "description": "The version of the API to use", @@ -16444,7 +15444,7 @@ }, "put": { "description": "Update agent by ID", - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D#1", + "operationId": "put-fleet-agents-agentid", "parameters": [ { "description": "The version of the API to use", @@ -16934,7 +15934,7 @@ "/api/fleet/agents/{agentId}/actions": { "post": { "description": "Create agent action", - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Factions#0", + "operationId": "post-fleet-agents-agentid-actions", "parameters": [ { "description": "The version of the API to use", @@ -17151,7 +16151,7 @@ "/api/fleet/agents/{agentId}/reassign": { "post": { "description": "Reassign agent", - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Freassign#1", + "operationId": "post-fleet-agents-agentid-reassign", "parameters": [ { "description": "The version of the API to use", @@ -17244,68 +16244,12 @@ "tags": [ "Elastic Agent actions" ] - }, - "put": { - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Freassign#0", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - }, - { - "description": "A required header to protect against CSRF attacks", - "in": "header", - "name": "kbn-xsrf", - "required": true, - "schema": { - "example": "true", - "type": "string" - } - }, - { - "in": "path", - "name": "agentId", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json; Elastic-Api-Version=2023-10-31": { - "schema": { - "additionalProperties": false, - "properties": { - "policy_id": { - "type": "string" - } - }, - "required": [ - "policy_id" - ], - "type": "object" - } - } - } - }, - "responses": {}, - "summary": "", - "tags": [] } }, "/api/fleet/agents/{agentId}/request_diagnostics": { "post": { "description": "Request agent diagnostics", - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Frequest_diagnostics#0", + "operationId": "post-fleet-agents-agentid-request-diagnostics", "parameters": [ { "description": "The version of the API to use", @@ -17414,7 +16358,7 @@ "/api/fleet/agents/{agentId}/unenroll": { "post": { "description": "Unenroll agent", - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Funenroll#0", + "operationId": "post-fleet-agents-agentid-unenroll", "parameters": [ { "description": "The version of the API to use", @@ -17476,7 +16420,7 @@ "/api/fleet/agents/{agentId}/upgrade": { "post": { "description": "Upgrade agent", - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Fupgrade#0", + "operationId": "post-fleet-agents-agentid-upgrade", "parameters": [ { "description": "The version of the API to use", @@ -17583,7 +16527,7 @@ "/api/fleet/agents/{agentId}/uploads": { "get": { "description": "List agent uploads", - "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Fuploads#0", + "operationId": "get-fleet-agents-agentid-uploads", "parameters": [ { "description": "The version of the API to use", @@ -17703,7 +16647,7 @@ "/api/fleet/check-permissions": { "get": { "description": "Check permissions", - "operationId": "%2Fapi%2Ffleet%2Fcheck-permissions#0", + "operationId": "get-fleet-check-permissions", "parameters": [ { "description": "The version of the API to use", @@ -17788,7 +16732,7 @@ "/api/fleet/data_streams": { "get": { "description": "List data streams", - "operationId": "%2Fapi%2Ffleet%2Fdata_streams#0", + "operationId": "get-fleet-data-streams", "parameters": [ { "description": "The version of the API to use", @@ -17945,7 +16889,7 @@ }, "/api/fleet/enrollment-api-keys": { "get": { - "operationId": "%2Fapi%2Ffleet%2Fenrollment-api-keys#0", + "operationId": "get-fleet-enrollment-api-keys-2", "parameters": [ { "description": "The version of the API to use", @@ -17991,7 +16935,7 @@ "tags": [] }, "post": { - "operationId": "%2Fapi%2Ffleet%2Fenrollment-api-keys#1", + "operationId": "post-fleet-enrollment-api-keys-2", "parameters": [ { "description": "The version of the API to use", @@ -18047,7 +16991,7 @@ }, "/api/fleet/enrollment-api-keys/{keyId}": { "delete": { - "operationId": "%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#1", + "operationId": "delete-fleet-enrollment-api-keys-keyid-2", "parameters": [ { "description": "The version of the API to use", @@ -18085,7 +17029,7 @@ "tags": [] }, "get": { - "operationId": "%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#0", + "operationId": "get-fleet-enrollment-api-keys-keyid-2", "parameters": [ { "description": "The version of the API to use", @@ -18116,7 +17060,7 @@ "/api/fleet/enrollment_api_keys": { "get": { "description": "List enrollment API keys", - "operationId": "%2Fapi%2Ffleet%2Fenrollment_api_keys#0", + "operationId": "get-fleet-enrollment-api-keys", "parameters": [ { "description": "The version of the API to use", @@ -18304,7 +17248,7 @@ }, "post": { "description": "Create enrollment API key", - "operationId": "%2Fapi%2Ffleet%2Fenrollment_api_keys#1", + "operationId": "post-fleet-enrollment-api-keys", "parameters": [ { "description": "The version of the API to use", @@ -18450,7 +17394,7 @@ "/api/fleet/enrollment_api_keys/{keyId}": { "delete": { "description": "Revoke enrollment API key by ID by marking it as inactive", - "operationId": "%2Fapi%2Ffleet%2Fenrollment_api_keys%2F%7BkeyId%7D#1", + "operationId": "delete-fleet-enrollment-api-keys-keyid", "parameters": [ { "description": "The version of the API to use", @@ -18538,7 +17482,7 @@ }, "get": { "description": "Get enrollment API key by ID", - "operationId": "%2Fapi%2Ffleet%2Fenrollment_api_keys%2F%7BkeyId%7D#0", + "operationId": "get-fleet-enrollment-api-keys-keyid", "parameters": [ { "description": "The version of the API to use", @@ -18651,7 +17595,7 @@ "/api/fleet/epm/bulk_assets": { "post": { "description": "Bulk get assets", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fbulk_assets#0", + "operationId": "post-fleet-epm-bulk-assets", "parameters": [ { "description": "The version of the API to use", @@ -18802,7 +17746,7 @@ "/api/fleet/epm/categories": { "get": { "description": "List package categories", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fcategories#0", + "operationId": "get-fleet-epm-categories", "parameters": [ { "description": "The version of the API to use", @@ -18951,7 +17895,7 @@ "/api/fleet/epm/custom_integrations": { "post": { "description": "Create custom integration", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fcustom_integrations#0", + "operationId": "post-fleet-epm-custom-integrations", "parameters": [ { "description": "The version of the API to use", @@ -19233,7 +18177,7 @@ "/api/fleet/epm/data_streams": { "get": { "description": "List data streams", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fdata_streams#0", + "operationId": "get-fleet-epm-data-streams", "parameters": [ { "description": "The version of the API to use", @@ -19359,7 +18303,7 @@ "/api/fleet/epm/packages": { "get": { "description": "List packages", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages#0", + "operationId": "get-fleet-epm-packages", "parameters": [ { "description": "The version of the API to use", @@ -20428,7 +19372,7 @@ }, "post": { "description": "Install package by upload", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages#1", + "operationId": "post-fleet-epm-packages", "parameters": [ { "description": "The version of the API to use", @@ -20690,7 +19634,7 @@ "/api/fleet/epm/packages/_bulk": { "post": { "description": "Bulk install packages", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F_bulk#0", + "operationId": "post-fleet-epm-packages-bulk", "parameters": [ { "description": "The version of the API to use", @@ -21114,7 +20058,7 @@ "/api/fleet/epm/packages/installed": { "get": { "description": "Get installed packages", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2Finstalled#0", + "operationId": "get-fleet-epm-packages-installed", "parameters": [ { "description": "The version of the API to use", @@ -21355,7 +20299,7 @@ "/api/fleet/epm/packages/limited": { "get": { "description": "Get limited package list", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2Flimited#0", + "operationId": "get-fleet-epm-packages-limited", "parameters": [ { "description": "The version of the API to use", @@ -21434,7 +20378,7 @@ "/api/fleet/epm/packages/{pkgName}/stats": { "get": { "description": "Get package stats", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2Fstats#0", + "operationId": "get-fleet-epm-packages-pkgname-stats", "parameters": [ { "description": "The version of the API to use", @@ -21520,7 +20464,7 @@ "/api/fleet/epm/packages/{pkgName}/{pkgVersion}": { "delete": { "description": "Delete package", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D#3", + "operationId": "delete-fleet-epm-packages-pkgname-pkgversion", "parameters": [ { "description": "The version of the API to use", @@ -21782,7 +20726,7 @@ }, "get": { "description": "Get package", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D#0", + "operationId": "get-fleet-epm-packages-pkgname-pkgversion", "parameters": [ { "description": "The version of the API to use", @@ -23046,7 +21990,7 @@ }, "post": { "description": "Install package from registry", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D#2", + "operationId": "post-fleet-epm-packages-pkgname-pkgversion", "parameters": [ { "description": "The version of the API to use", @@ -23341,7 +22285,7 @@ }, "put": { "description": "Update package settings", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D#1", + "operationId": "put-fleet-epm-packages-pkgname-pkgversion", "parameters": [ { "description": "The version of the API to use", @@ -24590,7 +23534,7 @@ "/api/fleet/epm/packages/{pkgName}/{pkgVersion}/transforms/authorize": { "post": { "description": "Authorize transforms", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D%2Ftransforms%2Fauthorize#0", + "operationId": "post-fleet-epm-packages-pkgname-pkgversion-transforms-authorize", "parameters": [ { "description": "The version of the API to use", @@ -24734,7 +23678,7 @@ "/api/fleet/epm/packages/{pkgName}/{pkgVersion}/{filePath*}": { "get": { "description": "Get package file", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D%2F%7BfilePath*%7D#0", + "operationId": "get-fleet-epm-packages-pkgname-pkgversion-filepath", "parameters": [ { "description": "The version of the API to use", @@ -24815,7 +23759,7 @@ }, "/api/fleet/epm/packages/{pkgkey}": { "delete": { - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#3", + "operationId": "delete-fleet-epm-packages-pkgkey", "parameters": [ { "description": "The version of the API to use", @@ -24872,7 +23816,7 @@ "tags": [] }, "get": { - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#0", + "operationId": "get-fleet-epm-packages-pkgkey", "parameters": [ { "description": "The version of the API to use", @@ -24933,7 +23877,7 @@ "tags": [] }, "post": { - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#2", + "operationId": "post-fleet-epm-packages-pkgkey", "parameters": [ { "description": "The version of the API to use", @@ -25016,7 +23960,7 @@ "tags": [] }, "put": { - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#1", + "operationId": "put-fleet-epm-packages-pkgkey", "parameters": [ { "description": "The version of the API to use", @@ -25075,7 +24019,7 @@ "/api/fleet/epm/templates/{pkgName}/{pkgVersion}/inputs": { "get": { "description": "Get inputs template", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Ftemplates%2F%7BpkgName%7D%2F%7BpkgVersion%7D%2Finputs#0", + "operationId": "get-fleet-epm-templates-pkgname-pkgversion-inputs", "parameters": [ { "description": "The version of the API to use", @@ -25244,7 +24188,7 @@ "/api/fleet/epm/verification_key_id": { "get": { "description": "Get a package signature verification key ID", - "operationId": "%2Fapi%2Ffleet%2Fepm%2Fverification_key_id#0", + "operationId": "get-fleet-epm-verification-key-id", "parameters": [ { "description": "The version of the API to use", @@ -25314,7 +24258,7 @@ "/api/fleet/fleet_server_hosts": { "get": { "description": "List Fleet Server hosts", - "operationId": "%2Fapi%2Ffleet%2Ffleet_server_hosts#0", + "operationId": "get-fleet-fleet-server-hosts", "parameters": [ { "description": "The version of the API to use", @@ -25432,7 +24376,7 @@ }, "post": { "description": "Create Fleet Server host", - "operationId": "%2Fapi%2Ffleet%2Ffleet_server_hosts#1", + "operationId": "post-fleet-fleet-server-hosts", "parameters": [ { "description": "The version of the API to use", @@ -25591,7 +24535,7 @@ "/api/fleet/fleet_server_hosts/{itemId}": { "delete": { "description": "Delete Fleet Server host by ID", - "operationId": "%2Fapi%2Ffleet%2Ffleet_server_hosts%2F%7BitemId%7D#1", + "operationId": "delete-fleet-fleet-server-hosts-itemid", "parameters": [ { "description": "The version of the API to use", @@ -25676,7 +24620,7 @@ }, "get": { "description": "Get Fleet Server host by ID", - "operationId": "%2Fapi%2Ffleet%2Ffleet_server_hosts%2F%7BitemId%7D#0", + "operationId": "get-fleet-fleet-server-hosts-itemid", "parameters": [ { "description": "The version of the API to use", @@ -25787,7 +24731,7 @@ }, "put": { "description": "Update Fleet Server host by ID", - "operationId": "%2Fapi%2Ffleet%2Ffleet_server_hosts%2F%7BitemId%7D#2", + "operationId": "put-fleet-fleet-server-hosts-itemid", "parameters": [ { "description": "The version of the API to use", @@ -25945,7 +24889,7 @@ "/api/fleet/health_check": { "post": { "description": "Check Fleet Server health", - "operationId": "%2Fapi%2Ffleet%2Fhealth_check#0", + "operationId": "post-fleet-health-check", "parameters": [ { "description": "The version of the API to use", @@ -26081,7 +25025,7 @@ "/api/fleet/kubernetes": { "get": { "description": "Get full K8s agent manifest", - "operationId": "%2Fapi%2Ffleet%2Fkubernetes#0", + "operationId": "get-fleet-kubernetes", "parameters": [ { "description": "The version of the API to use", @@ -26173,7 +25117,7 @@ }, "/api/fleet/kubernetes/download": { "get": { - "operationId": "%2Fapi%2Ffleet%2Fkubernetes%2Fdownload#0", + "operationId": "get-fleet-kubernetes-download", "parameters": [ { "description": "The version of the API to use", @@ -26282,7 +25226,7 @@ "/api/fleet/logstash_api_keys": { "post": { "description": "Generate Logstash API key", - "operationId": "%2Fapi%2Ffleet%2Flogstash_api_keys#0", + "operationId": "post-fleet-logstash-api-keys", "parameters": [ { "description": "The version of the API to use", @@ -26361,7 +25305,7 @@ "/api/fleet/message_signing_service/rotate_key_pair": { "post": { "description": "Rotate fleet message signing key pair", - "operationId": "%2Fapi%2Ffleet%2Fmessage_signing_service%2Frotate_key_pair#0", + "operationId": "post-fleet-message-signing-service-rotate-key-pair", "parameters": [ { "description": "The version of the API to use", @@ -26474,7 +25418,7 @@ "/api/fleet/outputs": { "get": { "description": "List outputs", - "operationId": "%2Fapi%2Ffleet%2Foutputs#0", + "operationId": "get-fleet-outputs", "parameters": [ { "description": "The version of the API to use", @@ -27604,7 +26548,7 @@ }, "post": { "description": "Create output", - "operationId": "%2Fapi%2Ffleet%2Foutputs#1", + "operationId": "post-fleet-outputs", "parameters": [ { "description": "The version of the API to use", @@ -29788,7 +28732,7 @@ "/api/fleet/outputs/{outputId}": { "delete": { "description": "Delete output by ID", - "operationId": "%2Fapi%2Ffleet%2Foutputs%2F%7BoutputId%7D#2", + "operationId": "delete-fleet-outputs-outputid", "parameters": [ { "description": "The version of the API to use", @@ -29898,7 +28842,7 @@ }, "get": { "description": "Get output by ID", - "operationId": "%2Fapi%2Ffleet%2Foutputs%2F%7BoutputId%7D#0", + "operationId": "get-fleet-outputs-outputid", "parameters": [ { "description": "The version of the API to use", @@ -31021,7 +29965,7 @@ }, "put": { "description": "Update output by ID", - "operationId": "%2Fapi%2Ffleet%2Foutputs%2F%7BoutputId%7D#1", + "operationId": "put-fleet-outputs-outputid", "parameters": [ { "description": "The version of the API to use", @@ -33189,7 +32133,7 @@ "/api/fleet/outputs/{outputId}/health": { "get": { "description": "Get latest output health", - "operationId": "%2Fapi%2Ffleet%2Foutputs%2F%7BoutputId%7D%2Fhealth#0", + "operationId": "get-fleet-outputs-outputid-health", "parameters": [ { "description": "The version of the API to use", @@ -33277,7 +32221,7 @@ "/api/fleet/package_policies": { "get": { "description": "List package policies", - "operationId": "%2Fapi%2Ffleet%2Fpackage_policies#0", + "operationId": "get-fleet-package-policies", "parameters": [ { "description": "The version of the API to use", @@ -33992,7 +32936,7 @@ }, "post": { "description": "Create package policy", - "operationId": "%2Fapi%2Ffleet%2Fpackage_policies#1", + "operationId": "post-fleet-package-policies", "parameters": [ { "description": "The version of the API to use", @@ -35266,7 +34210,7 @@ "/api/fleet/package_policies/_bulk_get": { "post": { "description": "Bulk get package policies", - "operationId": "%2Fapi%2Ffleet%2Fpackage_policies%2F_bulk_get#0", + "operationId": "post-fleet-package-policies-bulk-get", "parameters": [ { "description": "The version of the API to use", @@ -35964,7 +34908,7 @@ "/api/fleet/package_policies/delete": { "post": { "description": "Bulk delete package policies", - "operationId": "%2Fapi%2Ffleet%2Fpackage_policies%2Fdelete#0", + "operationId": "post-fleet-package-policies-delete", "parameters": [ { "description": "The version of the API to use", @@ -36168,7 +35112,7 @@ "/api/fleet/package_policies/upgrade": { "post": { "description": "Upgrade package policy to a newer package version", - "operationId": "%2Fapi%2Ffleet%2Fpackage_policies%2Fupgrade#0", + "operationId": "post-fleet-package-policies-upgrade", "parameters": [ { "description": "The version of the API to use", @@ -36293,7 +35237,7 @@ "/api/fleet/package_policies/upgrade/dryrun": { "post": { "description": "Dry run package policy upgrade", - "operationId": "%2Fapi%2Ffleet%2Fpackage_policies%2Fupgrade%2Fdryrun#0", + "operationId": "post-fleet-package-policies-upgrade-dryrun", "parameters": [ { "description": "The version of the API to use", @@ -37479,7 +36423,7 @@ "/api/fleet/package_policies/{packagePolicyId}": { "delete": { "description": "Delete package policy by ID", - "operationId": "%2Fapi%2Ffleet%2Fpackage_policies%2F%7BpackagePolicyId%7D#2", + "operationId": "delete-fleet-package-policies-packagepolicyid", "parameters": [ { "description": "The version of the API to use", @@ -37572,7 +36516,7 @@ }, "get": { "description": "Get package policy by ID", - "operationId": "%2Fapi%2Ffleet%2Fpackage_policies%2F%7BpackagePolicyId%7D#0", + "operationId": "get-fleet-package-policies-packagepolicyid", "parameters": [ { "description": "The version of the API to use", @@ -38238,7 +37182,7 @@ }, "put": { "description": "Update package policy by ID", - "operationId": "%2Fapi%2Ffleet%2Fpackage_policies%2F%7BpackagePolicyId%7D#1", + "operationId": "put-fleet-package-policies-packagepolicyid", "parameters": [ { "description": "The version of the API to use", @@ -39512,7 +38456,7 @@ "/api/fleet/proxies": { "get": { "description": "List proxies", - "operationId": "%2Fapi%2Ffleet%2Fproxies#0", + "operationId": "get-fleet-proxies", "parameters": [ { "description": "The version of the API to use", @@ -39644,7 +38588,7 @@ }, "post": { "description": "Create proxy", - "operationId": "%2Fapi%2Ffleet%2Fproxies#1", + "operationId": "post-fleet-proxies", "parameters": [ { "description": "The version of the API to use", @@ -39831,7 +38775,7 @@ "/api/fleet/proxies/{itemId}": { "delete": { "description": "Delete proxy by ID", - "operationId": "%2Fapi%2Ffleet%2Fproxies%2F%7BitemId%7D#2", + "operationId": "delete-fleet-proxies-itemid", "parameters": [ { "description": "The version of the API to use", @@ -39916,7 +38860,7 @@ }, "get": { "description": "Get proxy by ID", - "operationId": "%2Fapi%2Ffleet%2Fproxies%2F%7BitemId%7D#1", + "operationId": "get-fleet-proxies-itemid", "parameters": [ { "description": "The version of the API to use", @@ -40041,7 +38985,7 @@ }, "put": { "description": "Update proxy by ID", - "operationId": "%2Fapi%2Ffleet%2Fproxies%2F%7BitemId%7D#0", + "operationId": "put-fleet-proxies-itemid", "parameters": [ { "description": "The version of the API to use", @@ -40228,43 +39172,10 @@ ] } }, - "/api/fleet/service-tokens": { - "post": { - "description": "Create a service token", - "operationId": "%2Fapi%2Ffleet%2Fservice-tokens#0", - "parameters": [ - { - "description": "The version of the API to use", - "in": "header", - "name": "elastic-api-version", - "schema": { - "default": "2023-10-31", - "enum": [ - "2023-10-31" - ], - "type": "string" - } - }, - { - "description": "A required header to protect against CSRF attacks", - "in": "header", - "name": "kbn-xsrf", - "required": true, - "schema": { - "example": "true", - "type": "string" - } - } - ], - "responses": {}, - "summary": "", - "tags": [] - } - }, "/api/fleet/service_tokens": { "post": { "description": "Create a service token", - "operationId": "%2Fapi%2Ffleet%2Fservice_tokens#0", + "operationId": "post-fleet-service-tokens", "parameters": [ { "description": "The version of the API to use", @@ -40364,7 +39275,7 @@ "/api/fleet/settings": { "get": { "description": "Get settings", - "operationId": "%2Fapi%2Ffleet%2Fsettings#0", + "operationId": "get-fleet-settings", "parameters": [ { "description": "The version of the API to use", @@ -40515,7 +39426,7 @@ }, "put": { "description": "Update settings", - "operationId": "%2Fapi%2Ffleet%2Fsettings#1", + "operationId": "put-fleet-settings", "parameters": [ { "description": "The version of the API to use", @@ -40732,7 +39643,7 @@ "/api/fleet/setup": { "post": { "description": "Initiate Fleet setup", - "operationId": "%2Fapi%2Ffleet%2Fsetup#0", + "operationId": "post-fleet-setup", "parameters": [ { "description": "The version of the API to use", @@ -40851,7 +39762,7 @@ "/api/fleet/uninstall_tokens": { "get": { "description": "List metadata for latest uninstall tokens per agent policy", - "operationId": "%2Fapi%2Ffleet%2Funinstall_tokens#0", + "operationId": "get-fleet-uninstall-tokens", "parameters": [ { "description": "The version of the API to use", @@ -41000,7 +39911,7 @@ "/api/fleet/uninstall_tokens/{uninstallTokenId}": { "get": { "description": "Get one decrypted uninstall token by its ID", - "operationId": "%2Fapi%2Ffleet%2Funinstall_tokens%2F%7BuninstallTokenId%7D#0", + "operationId": "get-fleet-uninstall-tokens-uninstalltokenid", "parameters": [ { "description": "The version of the API to use", @@ -41107,7 +40018,7 @@ }, "/api/security/role": { "get": { - "operationId": "%2Fapi%2Fsecurity%2Frole#0", + "operationId": "get-security-role", "parameters": [ { "description": "The version of the API to use", @@ -41144,7 +40055,7 @@ }, "/api/security/role/{name}": { "delete": { - "operationId": "%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#1", + "operationId": "delete-security-role-name", "parameters": [ { "description": "The version of the API to use", @@ -41189,7 +40100,7 @@ ] }, "get": { - "operationId": "%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#0", + "operationId": "get-security-role-name", "parameters": [ { "description": "The version of the API to use", @@ -41235,7 +40146,7 @@ }, "put": { "description": "Create a new Kibana role or update the attributes of an existing role. Kibana roles are stored in the Elasticsearch native realm.", - "operationId": "%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#2", + "operationId": "put-security-role-name", "parameters": [ { "description": "The version of the API to use", @@ -41554,7 +40465,7 @@ }, "/api/security/roles": { "post": { - "operationId": "%2Fapi%2Fsecurity%2Froles#0", + "operationId": "post-security-roles", "parameters": [ { "description": "The version of the API to use", @@ -41864,7 +40775,7 @@ }, "/api/spaces/space": { "get": { - "operationId": "%2Fapi%2Fspaces%2Fspace#0", + "operationId": "get-spaces-space", "parameters": [ { "description": "The version of the API to use", @@ -41944,7 +40855,7 @@ ] }, "post": { - "operationId": "%2Fapi%2Fspaces%2Fspace#1", + "operationId": "post-spaces-space", "parameters": [ { "description": "The version of the API to use", @@ -42036,7 +40947,7 @@ "/api/spaces/space/{id}": { "delete": { "description": "When you delete a space, all saved objects that belong to the space are automatically deleted, which is permanent and cannot be undone.", - "operationId": "%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#2", + "operationId": "delete-spaces-space-id", "parameters": [ { "description": "The version of the API to use", @@ -42084,7 +40995,7 @@ ] }, "get": { - "operationId": "%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#0", + "operationId": "get-spaces-space-id", "parameters": [ { "description": "The version of the API to use", @@ -42119,7 +41030,7 @@ ] }, "put": { - "operationId": "%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#1", + "operationId": "put-spaces-space-id", "parameters": [ { "description": "The version of the API to use", @@ -42219,7 +41130,7 @@ }, "/api/status": { "get": { - "operationId": "%2Fapi%2Fstatus#0", + "operationId": "get-status", "parameters": [ { "description": "The version of the API to use", diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml index 583f4dbc4482c..afb7d8bbd5f4d 100644 --- a/oas_docs/output/kibana.serverless.yaml +++ b/oas_docs/output/kibana.serverless.yaml @@ -82,396 +82,10 @@ servers: - description: local url: http://localhost:5601 paths: - /api/actions: - get: - deprecated: true - operationId: '%2Fapi%2Factions#0' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - responses: {} - summary: Get all connectors - tags: - - connectors - /api/actions/action: - post: - deprecated: true - operationId: '%2Fapi%2Factions%2Faction#0' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - - description: A required header to protect against CSRF attacks - in: header - name: kbn-xsrf - required: true - schema: - example: 'true' - type: string - requestBody: - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - additionalProperties: false - type: object - properties: - actionTypeId: - description: The connector type identifier. - type: string - config: - additionalProperties: {} - default: {} - type: object - name: - description: The display name for the connector. - type: string - secrets: - additionalProperties: {} - default: {} - type: object - required: - - name - - actionTypeId - responses: - '200': - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - additionalProperties: false - type: object - properties: - config: - additionalProperties: {} - type: object - connector_type_id: - description: The connector type identifier. - type: string - id: - description: The identifier for the connector. - type: string - is_deprecated: - description: Indicates whether the connector is deprecated. - type: boolean - is_missing_secrets: - description: Indicates whether the connector is missing secrets. - type: boolean - is_preconfigured: - description: >- - Indicates whether the connector is preconfigured. If true, - the `config` and `is_missing_secrets` properties are - omitted from the response. - type: boolean - is_system_action: - description: >- - Indicates whether the connector is used for system - actions. - type: boolean - name: - description: ' The name of the rule.' - type: string - required: - - id - - name - - connector_type_id - - is_preconfigured - - is_deprecated - - is_system_action - description: Indicates a successful call. - summary: Create a connector - tags: - - connectors - /api/actions/action/{id}: - delete: - deprecated: true - description: 'WARNING: When you delete a connector, it cannot be recovered.' - operationId: '%2Fapi%2Factions%2Faction%2F%7Bid%7D#0' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - - description: A required header to protect against CSRF attacks - in: header - name: kbn-xsrf - required: true - schema: - example: 'true' - type: string - - description: An identifier for the connector. - in: path - name: id - required: true - schema: - type: string - responses: - '204': - description: Indicates a successful call. - summary: Delete a connector - tags: - - connectors - get: - deprecated: true - operationId: '%2Fapi%2Factions%2Faction%2F%7Bid%7D#1' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - - description: An identifier for the connector. - in: path - name: id - required: true - schema: - type: string - responses: - '200': - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - additionalProperties: false - type: object - properties: - config: - additionalProperties: {} - type: object - connector_type_id: - description: The connector type identifier. - type: string - id: - description: The identifier for the connector. - type: string - is_deprecated: - description: Indicates whether the connector is deprecated. - type: boolean - is_missing_secrets: - description: Indicates whether the connector is missing secrets. - type: boolean - is_preconfigured: - description: >- - Indicates whether the connector is preconfigured. If true, - the `config` and `is_missing_secrets` properties are - omitted from the response. - type: boolean - is_system_action: - description: >- - Indicates whether the connector is used for system - actions. - type: boolean - name: - description: ' The name of the rule.' - type: string - required: - - id - - name - - connector_type_id - - is_preconfigured - - is_deprecated - - is_system_action - description: Indicates a successful call. - summary: Get connector information - tags: - - connectors - put: - deprecated: true - operationId: '%2Fapi%2Factions%2Faction%2F%7Bid%7D#2' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - - description: A required header to protect against CSRF attacks - in: header - name: kbn-xsrf - required: true - schema: - example: 'true' - type: string - - description: An identifier for the connector. - in: path - name: id - required: true - schema: - type: string - requestBody: - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - additionalProperties: false - type: object - properties: - config: - additionalProperties: {} - default: {} - type: object - name: - type: string - secrets: - additionalProperties: {} - default: {} - type: object - required: - - name - responses: - '200': - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - additionalProperties: false - type: object - properties: - config: - additionalProperties: {} - type: object - connector_type_id: - description: The connector type identifier. - type: string - id: - description: The identifier for the connector. - type: string - is_deprecated: - description: Indicates whether the connector is deprecated. - type: boolean - is_missing_secrets: - description: Indicates whether the connector is missing secrets. - type: boolean - is_preconfigured: - description: >- - Indicates whether the connector is preconfigured. If true, - the `config` and `is_missing_secrets` properties are - omitted from the response. - type: boolean - is_system_action: - description: >- - Indicates whether the connector is used for system - actions. - type: boolean - name: - description: ' The name of the rule.' - type: string - required: - - id - - name - - connector_type_id - - is_preconfigured - - is_deprecated - - is_system_action - description: Indicates a successful call. - summary: Update a connector - tags: - - connectors - /api/actions/action/{id}/_execute: - post: - deprecated: true - operationId: '%2Fapi%2Factions%2Faction%2F%7Bid%7D%2F_execute#0' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - - description: A required header to protect against CSRF attacks - in: header - name: kbn-xsrf - required: true - schema: - example: 'true' - type: string - - description: An identifier for the connector. - in: path - name: id - required: true - schema: - type: string - requestBody: - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - additionalProperties: false - type: object - properties: - params: - additionalProperties: {} - type: object - required: - - params - responses: - '200': - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - additionalProperties: false - type: object - properties: - config: - additionalProperties: {} - type: object - connector_type_id: - description: The connector type identifier. - type: string - id: - description: The identifier for the connector. - type: string - is_deprecated: - description: Indicates whether the connector is deprecated. - type: boolean - is_missing_secrets: - description: Indicates whether the connector is missing secrets. - type: boolean - is_preconfigured: - description: >- - Indicates whether the connector is preconfigured. If true, - the `config` and `is_missing_secrets` properties are - omitted from the response. - type: boolean - is_system_action: - description: >- - Indicates whether the connector is used for system - actions. - type: boolean - name: - description: ' The name of the rule.' - type: string - required: - - id - - name - - connector_type_id - - is_preconfigured - - is_deprecated - - is_system_action - description: Indicates a successful call. - summary: Run a connector - tags: - - connectors /api/actions/connector_types: get: description: You do not need any Kibana feature privileges to run this API. - operationId: '%2Fapi%2Factions%2Fconnector_types#0' + operationId: get-actions-connector-types parameters: - description: The version of the API to use in: header @@ -496,7 +110,7 @@ paths: /api/actions/connector/{id}: delete: description: 'WARNING: When you delete a connector, it cannot be recovered.' - operationId: '%2Fapi%2Factions%2Fconnector%2F%7Bid%7D#0' + operationId: delete-actions-connector-id parameters: - description: The version of the API to use in: header @@ -526,7 +140,7 @@ paths: tags: - connectors get: - operationId: '%2Fapi%2Factions%2Fconnector%2F%7Bid%7D#1' + operationId: get-actions-connector-id parameters: - description: The version of the API to use in: header @@ -591,7 +205,7 @@ paths: tags: - connectors post: - operationId: '%2Fapi%2Factions%2Fconnector%2F%7Bid%3F%7D#0' + operationId: post-actions-connector-id parameters: - description: The version of the API to use in: header @@ -687,7 +301,7 @@ paths: tags: - connectors put: - operationId: '%2Fapi%2Factions%2Fconnector%2F%7Bid%7D#2' + operationId: put-actions-connector-id parameters: - description: The version of the API to use in: header @@ -783,7 +397,7 @@ paths: description: >- You can use this API to test an action that involves interaction with Kibana services or integrations with third-party systems. - operationId: '%2Fapi%2Factions%2Fconnector%2F%7Bid%7D%2F_execute#0' + operationId: post-actions-connector-id-execute parameters: - description: The version of the API to use in: header @@ -868,7 +482,7 @@ paths: - connectors /api/actions/connectors: get: - operationId: '%2Fapi%2Factions%2Fconnectors#0' + operationId: get-actions-connectors parameters: - description: The version of the API to use in: header @@ -882,26 +496,9 @@ paths: summary: Get all connectors tags: - connectors - /api/actions/list_action_types: - get: - deprecated: true - operationId: '%2Fapi%2Factions%2Flist_action_types#0' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - responses: {} - summary: Get connector types - tags: - - connectors /api/alerting/rule/{id}: delete: - operationId: '%2Fapi%2Falerting%2Frule%2F%7Bid%7D#2' + operationId: delete-alerting-rule-id parameters: - description: The version of the API to use in: header @@ -937,7 +534,7 @@ paths: tags: - alerting get: - operationId: '%2Fapi%2Falerting%2Frule%2F%7Bid%7D#0' + operationId: get-alerting-rule-id parameters: - description: The version of the API to use in: header @@ -1735,7 +1332,7 @@ paths: tags: - alerting post: - operationId: '%2Fapi%2Falerting%2Frule%2F%7Bid%3F%7D#0' + operationId: post-alerting-rule-id parameters: - description: The version of the API to use in: header @@ -2858,7 +2455,7 @@ paths: tags: - alerting put: - operationId: '%2Fapi%2Falerting%2Frule%2F%7Bid%7D#1' + operationId: put-alerting-rule-id parameters: - description: The version of the API to use in: header @@ -3955,7 +3552,7 @@ paths: - alerting /api/alerting/rule/{id}/_disable: post: - operationId: '%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_disable#0' + operationId: post-alerting-rule-id-disable parameters: - description: The version of the API to use in: header @@ -4004,7 +3601,7 @@ paths: - alerting /api/alerting/rule/{id}/_enable: post: - operationId: '%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_enable#0' + operationId: post-alerting-rule-id-enable parameters: - description: The version of the API to use in: header @@ -4041,7 +3638,7 @@ paths: - alerting /api/alerting/rule/{id}/_mute_all: post: - operationId: '%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_mute_all#0' + operationId: post-alerting-rule-id-mute-all parameters: - description: The version of the API to use in: header @@ -4078,7 +3675,7 @@ paths: - alerting /api/alerting/rule/{id}/_unmute_all: post: - operationId: '%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_unmute_all#0' + operationId: post-alerting-rule-id-unmute-all parameters: - description: The version of the API to use in: header @@ -4115,7 +3712,7 @@ paths: - alerting /api/alerting/rule/{id}/_update_api_key: post: - operationId: '%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_update_api_key#0' + operationId: post-alerting-rule-id-update-api-key parameters: - description: The version of the API to use in: header @@ -4154,8 +3751,7 @@ paths: - alerting /api/alerting/rule/{rule_id}/alert/{alert_id}/_mute: post: - operationId: >- - %2Fapi%2Falerting%2Frule%2F%7Brule_id%7D%2Falert%2F%7Balert_id%7D%2F_mute#0 + operationId: post-alerting-rule-rule-id-alert-alert-id-mute parameters: - description: The version of the API to use in: header @@ -4198,8 +3794,7 @@ paths: - alerting /api/alerting/rule/{rule_id}/alert/{alert_id}/_unmute: post: - operationId: >- - %2Fapi%2Falerting%2Frule%2F%7Brule_id%7D%2Falert%2F%7Balert_id%7D%2F_unmute#0 + operationId: post-alerting-rule-rule-id-alert-alert-id-unmute parameters: - description: The version of the API to use in: header @@ -4242,7 +3837,7 @@ paths: - alerting /api/alerting/rules/_find: get: - operationId: '%2Fapi%2Falerting%2Frules%2F_find#0' + operationId: get-alerting-rules-find parameters: - description: The version of the API to use in: header @@ -5141,49 +4736,110 @@ paths: post: description: Create a new agent key for APM. operationId: createAgentKey + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - $ref: '#/components/parameters/APM_UI_kbn_xsrf' requestBody: content: application/json; Elastic-Api-Version=2023-10-31: schema: - type: object - properties: - name: - type: string - privileges: - items: - enum: - - event:write - - config_agent:read - type: string - type: array + $ref: '#/components/schemas/APM_UI_agent_keys_object' required: true responses: '200': content: application/json; Elastic-Api-Version=2023-10-31: schema: - type: object - properties: - api_key: - type: string - encoded: - type: string - expiration: - format: int64 - type: integer - id: - type: string - name: - type: string + $ref: '#/components/schemas/APM_UI_agent_keys_response' description: Agent key created successfully + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '403': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_403_response' + description: Forbidden response + '500': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_500_response' + description: Internal Server Error response summary: Create an APM agent key tags: - APM agent keys + /api/apm/fleet/apm_server_schema: + post: + operationId: saveApmServerSchema + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - $ref: '#/components/parameters/APM_UI_kbn_xsrf' + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + type: object + properties: + schema: + additionalProperties: true + description: Schema object + example: + foo: bar + type: object + required: true + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '403': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_403_response' + description: Forbidden response + '404': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_404_response' + description: Not found response + summary: Save APM server schema + tags: + - APM server schema /api/apm/services/{serviceName}/annotation: post: description: Create a new annotation for a specific service. operationId: createAnnotation parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - $ref: '#/components/parameters/APM_UI_kbn_xsrf' - description: The name of the service in: path name: serviceName @@ -5194,63 +4850,39 @@ paths: content: application/json; Elastic-Api-Version=2023-10-31: schema: - type: object - properties: - '@timestamp': - type: string - message: - type: string - service: - type: object - properties: - environment: - type: string - version: - type: string - tags: - items: - type: string - type: array + $ref: '#/components/schemas/APM_UI_create_annotation_object' required: true responses: '200': content: application/json; Elastic-Api-Version=2023-10-31: schema: - type: object - properties: - _id: - type: string - _index: - type: string - _source: - type: object - properties: - '@timestamp': - type: string - annotation: - type: string - event: - type: object - properties: - created: - type: string - message: - type: string - service: - type: object - properties: - environment: - type: string - name: - type: string - version: - type: string - tags: - items: - type: string - type: array + $ref: '#/components/schemas/APM_UI_create_annotation_response' description: Annotation created successfully + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '403': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_403_response' + description: Forbidden response + '404': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_404_response' + description: Not found response summary: Create a service annotation tags: - APM annotations @@ -5259,6 +4891,7 @@ paths: description: Search for annotations related to a specific service. operationId: getAnnotation parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' - description: The name of the service in: path name: serviceName @@ -5288,27 +4921,484 @@ paths: content: application/json; Elastic-Api-Version=2023-10-31: schema: - type: object - properties: - annotations: - items: - type: object - properties: - '@timestamp': - type: number - id: - type: string - text: - type: string - type: - enum: - - version - type: string - type: array + $ref: '#/components/schemas/APM_UI_annotation_search_response' description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '500': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_500_response' + description: Internal Server Error response summary: Search for annotations tags: - APM annotations + /api/apm/settings/agent-configuration: + delete: + operationId: deleteAgentConfiguration + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - $ref: '#/components/parameters/APM_UI_kbn_xsrf' + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_service_object' + required: true + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: >- + #/components/schemas/APM_UI_delete_agent_configurations_response + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '403': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_403_response' + description: Forbidden response + '404': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_404_response' + description: Not found response + summary: Delete agent configuration + tags: + - APM agent configuration + get: + operationId: getAgentConfigurations + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_agent_configurations_response' + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '404': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_404_response' + description: Not found response + summary: Get a list of agent configurations + tags: + - APM agent configuration + put: + operationId: createUpdateAgentConfiguration + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - $ref: '#/components/parameters/APM_UI_kbn_xsrf' + - description: If the config exists ?overwrite=true is required + in: query + name: overwrite + schema: + type: boolean + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_agent_configuration_intake_object' + required: true + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '403': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_403_response' + description: Forbidden response + '404': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_404_response' + description: Not found response + summary: Create or update agent configuration + tags: + - APM agent configuration + /api/apm/settings/agent-configuration/agent_name: + get: + description: Retrieve `agentName` for a service. + operationId: getAgentNameForService + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - description: The name of the service + example: node + in: query + name: serviceName + required: true + schema: + type: string + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_service_agent_name_response' + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '404': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_404_response' + description: Not found response + summary: Get agent name for service + tags: + - APM agent configuration + /api/apm/settings/agent-configuration/environments: + get: + operationId: getEnvironmentsForService + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - description: The name of the service + in: query + name: serviceName + schema: + type: string + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_service_environments_response' + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '404': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_404_response' + description: Not found response + summary: Get environments for service + tags: + - APM agent configuration + /api/apm/settings/agent-configuration/search: + post: + description: > + This endpoint allows to search for single agent configuration and update + 'applied_by_agent' field. + operationId: searchSingleConfiguration + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - $ref: '#/components/parameters/APM_UI_kbn_xsrf' + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_search_agent_configuration_object' + required: true + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: >- + #/components/schemas/APM_UI_search_agent_configuration_response + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '404': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_404_response' + description: Not found response + summary: Lookup single agent configuration + tags: + - APM agent configuration + /api/apm/settings/agent-configuration/view: + get: + operationId: getSingleAgentConfiguration + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - description: Service name + example: node + in: query + name: name + schema: + type: string + - description: Service environment + example: prod + in: query + name: environment + schema: + type: string + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: >- + #/components/schemas/APM_UI_single_agent_configuration_response + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '404': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_404_response' + description: Not found response + summary: Get single agent configuration + tags: + - APM agent configuration + /api/apm/sourcemaps: + get: + description: Returns an array of Fleet artifacts, including source map uploads. + operationId: getSourceMaps + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - description: Page number + in: query + name: page + schema: + type: number + - description: Number of records per page + in: query + name: perPage + schema: + type: number + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_source_maps_response' + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '500': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_500_response' + description: Internal Server Error response + '501': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_501_response' + description: Not Implemented response + summary: Get source maps + tags: + - APM sourcemaps + post: + description: Upload a source map for a specific service and version. + operationId: uploadSourceMap + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - $ref: '#/components/parameters/APM_UI_kbn_xsrf' + requestBody: + content: + multipart/form-data; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_upload_source_map_object' + required: true + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_upload_source_maps_response' + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '403': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_403_response' + description: Forbidden response + '500': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_500_response' + description: Internal Server Error response + '501': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_501_response' + description: Not Implemented response + summary: Upload source map + tags: + - APM sourcemaps + /api/apm/sourcemaps/{id}: + delete: + description: Delete a previously uploaded source map. + operationId: deleteSourceMap + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - $ref: '#/components/parameters/APM_UI_kbn_xsrf' + - description: Source map identifier + in: path + name: id + required: true + schema: + type: string + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '403': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_403_response' + description: Forbidden response + '500': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_500_response' + description: Internal Server Error response + '501': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_501_response' + description: Not Implemented response + summary: Delete source map + tags: + - APM sourcemaps /api/asset_criticality: delete: description: Delete the asset criticality record for a specific entity. @@ -9781,7 +9871,7 @@ paths: /api/fleet/agent_download_sources: get: description: List agent binary download sources - operationId: '%2Fapi%2Ffleet%2Fagent_download_sources#0' + operationId: get-fleet-agent-download-sources parameters: - description: The version of the API to use in: header @@ -9857,7 +9947,7 @@ paths: - Elastic Agent binary download sources post: description: Create agent binary download source - operationId: '%2Fapi%2Ffleet%2Fagent_download_sources#1' + operationId: post-fleet-agent-download-sources parameters: - description: The version of the API to use in: header @@ -9956,7 +10046,7 @@ paths: /api/fleet/agent_download_sources/{sourceId}: delete: description: Delete agent binary download source by ID - operationId: '%2Fapi%2Ffleet%2Fagent_download_sources%2F%7BsourceId%7D#2' + operationId: delete-fleet-agent-download-sources-sourceid parameters: - description: The version of the API to use in: header @@ -10011,7 +10101,7 @@ paths: - Elastic Agent binary download sources get: description: Get agent binary download source by ID - operationId: '%2Fapi%2Ffleet%2Fagent_download_sources%2F%7BsourceId%7D#0' + operationId: get-fleet-agent-download-sources-sourceid parameters: - description: The version of the API to use in: header @@ -10081,7 +10171,7 @@ paths: - Elastic Agent binary download sources put: description: Update agent binary download source by ID - operationId: '%2Fapi%2Ffleet%2Fagent_download_sources%2F%7BsourceId%7D#1' + operationId: put-fleet-agent-download-sources-sourceid parameters: - description: The version of the API to use in: header @@ -10185,7 +10275,7 @@ paths: /api/fleet/agent_policies: get: description: List agent policies - operationId: '%2Fapi%2Ffleet%2Fagent_policies#0' + operationId: get-fleet-agent-policies parameters: - description: The version of the API to use in: header @@ -10799,7 +10889,7 @@ paths: - Elastic Agent policies post: description: Create an agent policy - operationId: '%2Fapi%2Ffleet%2Fagent_policies#1' + operationId: post-fleet-agent-policies parameters: - description: The version of the API to use in: header @@ -11534,7 +11624,7 @@ paths: /api/fleet/agent_policies/_bulk_get: post: description: Bulk get agent policies - operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F_bulk_get#0' + operationId: post-fleet-agent-policies-bulk-get parameters: - description: The version of the API to use in: header @@ -12114,7 +12204,7 @@ paths: /api/fleet/agent_policies/{agentPolicyId}: get: description: Get an agent policy by ID - operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D#0' + operationId: get-fleet-agent-policies-agentpolicyid parameters: - description: The version of the API to use in: header @@ -12670,7 +12760,7 @@ paths: - Elastic Agent policies put: description: Update an agent policy by ID - operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D#1' + operationId: put-fleet-agent-policies-agentpolicyid parameters: - description: The version of the API to use in: header @@ -13413,7 +13503,7 @@ paths: /api/fleet/agent_policies/{agentPolicyId}/copy: post: description: Copy an agent policy by ID - operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Fcopy#0' + operationId: post-fleet-agent-policies-agentpolicyid-copy parameters: - description: The version of the API to use in: header @@ -13991,7 +14081,7 @@ paths: /api/fleet/agent_policies/{agentPolicyId}/download: get: description: Download an agent policy by ID - operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Fdownload#0' + operationId: get-fleet-agent-policies-agentpolicyid-download parameters: - description: The version of the API to use in: header @@ -14065,7 +14155,7 @@ paths: /api/fleet/agent_policies/{agentPolicyId}/full: get: description: Get a full agent policy by ID - operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Ffull#0' + operationId: get-fleet-agent-policies-agentpolicyid-full parameters: - description: The version of the API to use in: header @@ -14397,7 +14487,7 @@ paths: /api/fleet/agent_policies/{agentPolicyId}/outputs: get: description: Get list of outputs associated with agent policy by policy id - operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Foutputs#0' + operationId: get-fleet-agent-policies-agentpolicyid-outputs parameters: - description: The version of the API to use in: header @@ -14501,7 +14591,7 @@ paths: /api/fleet/agent_policies/delete: post: description: Delete agent policy by ID - operationId: '%2Fapi%2Ffleet%2Fagent_policies%2Fdelete#0' + operationId: post-fleet-agent-policies-delete parameters: - description: The version of the API to use in: header @@ -14571,7 +14661,7 @@ paths: /api/fleet/agent_policies/outputs: post: description: Get list of outputs associated with agent policies - operationId: '%2Fapi%2Ffleet%2Fagent_policies%2Foutputs#0' + operationId: post-fleet-agent-policies-outputs parameters: - description: The version of the API to use in: header @@ -14693,7 +14783,7 @@ paths: /api/fleet/agent_status: get: description: Get agent status summary - operationId: '%2Fapi%2Ffleet%2Fagent_status#0' + operationId: get-fleet-agent-status parameters: - description: The version of the API to use in: header @@ -14721,7 +14811,6 @@ paths: name: kuery required: false schema: - deprecated: true type: string responses: '200': @@ -14751,16 +14840,12 @@ paths: type: number other: type: number - total: - deprecated: true - type: number unenrolled: type: number updating: type: number required: - events - - total - online - error - offline @@ -14794,7 +14879,7 @@ paths: /api/fleet/agent_status/data: get: description: Get incoming agent data - operationId: '%2Fapi%2Ffleet%2Fagent_status%2Fdata#0' + operationId: get-fleet-agent-status-data parameters: - description: The version of the API to use in: header @@ -14864,45 +14949,10 @@ paths: summary: '' tags: - Elastic Agents - /api/fleet/agent-status: - get: - operationId: '%2Fapi%2Ffleet%2Fagent-status#0' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - - in: query - name: policyId - required: false - schema: - type: string - - in: query - name: policyIds - required: false - schema: - anyOf: - - items: - type: string - type: array - - type: string - - in: query - name: kuery - required: false - schema: - deprecated: true - type: string - responses: {} - summary: '' - tags: [] /api/fleet/agents: get: description: List agents - operationId: '%2Fapi%2Ffleet%2Fagents#0' + operationId: get-fleet-agents parameters: - description: The version of the API to use in: header @@ -15252,285 +15302,6 @@ paths: - enrolled_at - local_metadata type: array - list: - deprecated: true - items: - additionalProperties: false - type: object - properties: - access_api_key: - type: string - access_api_key_id: - type: string - active: - type: boolean - agent: - additionalProperties: true - type: object - properties: - id: - type: string - version: - type: string - required: - - id - - version - components: - items: - additionalProperties: false - type: object - properties: - id: - type: string - message: - type: string - status: - enum: - - STARTING - - CONFIGURING - - HEALTHY - - DEGRADED - - FAILED - - STOPPING - - STOPPED - type: string - type: - type: string - units: - items: - additionalProperties: false - type: object - properties: - id: - type: string - message: - type: string - payload: - additionalProperties: {} - type: object - status: - enum: - - STARTING - - CONFIGURING - - HEALTHY - - DEGRADED - - FAILED - - STOPPING - - STOPPED - type: string - type: - enum: - - input - - output - type: string - required: - - id - - type - - status - - message - type: array - required: - - id - - type - - status - - message - type: array - default_api_key: - type: string - default_api_key_history: - items: - additionalProperties: false - deprecated: true - type: object - properties: - id: - type: string - retired_at: - type: string - required: - - id - - retired_at - type: array - default_api_key_id: - type: string - enrolled_at: - type: string - id: - type: string - last_checkin: - type: string - last_checkin_message: - type: string - last_checkin_status: - enum: - - error - - online - - degraded - - updating - - starting - type: string - local_metadata: - additionalProperties: {} - type: object - metrics: - additionalProperties: false - type: object - properties: - cpu_avg: - type: number - memory_size_byte_avg: - type: number - namespaces: - items: - type: string - type: array - outputs: - additionalProperties: - additionalProperties: false - type: object - properties: - api_key_id: - type: string - to_retire_api_key_ids: - items: - additionalProperties: false - type: object - properties: - id: - type: string - retired_at: - type: string - required: - - id - - retired_at - type: array - type: - type: string - required: - - api_key_id - - type - type: object - packages: - items: - type: string - type: array - policy_id: - type: string - policy_revision: - nullable: true - type: number - sort: - items: - anyOf: - - type: number - - type: string - - enum: [] - nullable: true - type: array - status: - enum: - - offline - - error - - online - - inactive - - enrolling - - unenrolling - - unenrolled - - updating - - degraded - type: string - tags: - items: - type: string - type: array - type: - enum: - - PERMANENT - - EPHEMERAL - - TEMPORARY - type: string - unenrolled_at: - type: string - unenrollment_started_at: - type: string - unhealthy_reason: - items: - enum: - - input - - output - - other - type: string - nullable: true - type: array - upgrade_details: - additionalProperties: false - type: object - properties: - action_id: - type: string - metadata: - additionalProperties: false - type: object - properties: - download_percent: - type: number - download_rate: - type: number - error_msg: - type: string - failed_state: - enum: - - UPG_REQUESTED - - UPG_SCHEDULED - - UPG_DOWNLOADING - - UPG_EXTRACTING - - UPG_REPLACING - - UPG_RESTARTING - - UPG_FAILED - - UPG_WATCHING - - UPG_ROLLBACK - type: string - retry_error_msg: - type: string - retry_until: - type: string - scheduled_at: - type: string - state: - enum: - - UPG_REQUESTED - - UPG_SCHEDULED - - UPG_DOWNLOADING - - UPG_EXTRACTING - - UPG_REPLACING - - UPG_RESTARTING - - UPG_FAILED - - UPG_WATCHING - - UPG_ROLLBACK - type: string - target_version: - type: string - required: - - target_version - - action_id - - state - upgrade_started_at: - nullable: true - type: string - upgraded_at: - nullable: true - type: string - user_provided_metadata: - additionalProperties: {} - type: object - required: - - id - - packages - - type - - active - - enrolled_at - - local_metadata - type: array page: type: number perPage: @@ -15567,7 +15338,7 @@ paths: - Elastic Agents post: description: List agents by action ids - operationId: '%2Fapi%2Ffleet%2Fagents#1' + operationId: post-fleet-agents parameters: - description: The version of the API to use in: header @@ -15633,7 +15404,7 @@ paths: /api/fleet/agents/{agentId}: delete: description: Delete agent by ID - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D#2' + operationId: delete-fleet-agents-agentid parameters: - description: The version of the API to use in: header @@ -15690,7 +15461,7 @@ paths: - Elastic Agents get: description: Get agent by ID - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D#0' + operationId: get-fleet-agents-agentid parameters: - description: The version of the API to use in: header @@ -16018,7 +15789,7 @@ paths: - Elastic Agents put: description: Update agent by ID - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D#1' + operationId: put-fleet-agents-agentid parameters: - description: The version of the API to use in: header @@ -16362,7 +16133,7 @@ paths: /api/fleet/agents/{agentId}/actions: post: description: Create agent action - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Factions#0' + operationId: post-fleet-agents-agentid-actions parameters: - description: The version of the API to use in: header @@ -16507,7 +16278,7 @@ paths: /api/fleet/agents/{agentId}/reassign: post: description: Reassign agent - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Freassign#1' + operationId: post-fleet-agents-agentid-reassign parameters: - description: The version of the API to use in: header @@ -16567,47 +16338,10 @@ paths: summary: '' tags: - Elastic Agent actions - put: - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Freassign#0' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - - description: A required header to protect against CSRF attacks - in: header - name: kbn-xsrf - required: true - schema: - example: 'true' - type: string - - in: path - name: agentId - required: true - schema: - type: string - requestBody: - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - additionalProperties: false - type: object - properties: - policy_id: - type: string - required: - - policy_id - responses: {} - summary: '' - tags: [] /api/fleet/agents/{agentId}/request_diagnostics: post: description: Request agent diagnostics - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Frequest_diagnostics#0' + operationId: post-fleet-agents-agentid-request-diagnostics parameters: - description: The version of the API to use in: header @@ -16677,7 +16411,7 @@ paths: /api/fleet/agents/{agentId}/unenroll: post: description: Unenroll agent - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Funenroll#0' + operationId: post-fleet-agents-agentid-unenroll parameters: - description: The version of the API to use in: header @@ -16718,7 +16452,7 @@ paths: /api/fleet/agents/{agentId}/upgrade: post: description: Upgrade agent - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Fupgrade#0' + operationId: post-fleet-agents-agentid-upgrade parameters: - description: The version of the API to use in: header @@ -16787,7 +16521,7 @@ paths: /api/fleet/agents/{agentId}/uploads: get: description: List agent uploads - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Fuploads#0' + operationId: get-fleet-agents-agentid-uploads parameters: - description: The version of the API to use in: header @@ -16868,7 +16602,7 @@ paths: /api/fleet/agents/action_status: get: description: Get agent action status - operationId: '%2Fapi%2Ffleet%2Fagents%2Faction_status#0' + operationId: get-fleet-agents-action-status parameters: - description: The version of the API to use in: header @@ -17036,7 +16770,7 @@ paths: /api/fleet/agents/actions/{actionId}/cancel: post: description: Cancel agent action - operationId: '%2Fapi%2Ffleet%2Fagents%2Factions%2F%7BactionId%7D%2Fcancel#0' + operationId: post-fleet-agents-actions-actionid-cancel parameters: - description: The version of the API to use in: header @@ -17131,7 +16865,7 @@ paths: /api/fleet/agents/available_versions: get: description: Get available agent versions - operationId: '%2Fapi%2Ffleet%2Fagents%2Favailable_versions#0' + operationId: get-fleet-agents-available-versions parameters: - description: The version of the API to use in: header @@ -17177,7 +16911,7 @@ paths: /api/fleet/agents/bulk_reassign: post: description: Bulk reassign agents - operationId: '%2Fapi%2Ffleet%2Fagents%2Fbulk_reassign#0' + operationId: post-fleet-agents-bulk-reassign parameters: - description: The version of the API to use in: header @@ -17251,7 +16985,7 @@ paths: /api/fleet/agents/bulk_request_diagnostics: post: description: Bulk request diagnostics from agents - operationId: '%2Fapi%2Ffleet%2Fagents%2Fbulk_request_diagnostics#0' + operationId: post-fleet-agents-bulk-request-diagnostics parameters: - description: The version of the API to use in: header @@ -17325,7 +17059,7 @@ paths: /api/fleet/agents/bulk_unenroll: post: description: Bulk unenroll agents - operationId: '%2Fapi%2Ffleet%2Fagents%2Fbulk_unenroll#0' + operationId: post-fleet-agents-bulk-unenroll parameters: - description: The version of the API to use in: header @@ -17406,7 +17140,7 @@ paths: /api/fleet/agents/bulk_update_agent_tags: post: description: Bulk update agent tags - operationId: '%2Fapi%2Ffleet%2Fagents%2Fbulk_update_agent_tags#0' + operationId: post-fleet-agents-bulk-update-agent-tags parameters: - description: The version of the API to use in: header @@ -17485,7 +17219,7 @@ paths: /api/fleet/agents/bulk_upgrade: post: description: Bulk upgrade agents - operationId: '%2Fapi%2Ffleet%2Fagents%2Fbulk_upgrade#0' + operationId: post-fleet-agents-bulk-upgrade parameters: - description: The version of the API to use in: header @@ -17570,7 +17304,7 @@ paths: /api/fleet/agents/files/{fileId}: delete: description: Delete file uploaded by agent - operationId: '%2Fapi%2Ffleet%2Fagents%2Ffiles%2F%7BfileId%7D#0' + operationId: delete-fleet-agents-files-fileid parameters: - description: The version of the API to use in: header @@ -17629,7 +17363,7 @@ paths: /api/fleet/agents/files/{fileId}/{fileName}: get: description: Get file uploaded by agent - operationId: '%2Fapi%2Ffleet%2Fagents%2Ffiles%2F%7BfileId%7D%2F%7BfileName%7D#0' + operationId: get-fleet-agents-files-fileid-filename parameters: - description: The version of the API to use in: header @@ -17677,7 +17411,7 @@ paths: /api/fleet/agents/setup: get: description: Get agent setup info - operationId: '%2Fapi%2Ffleet%2Fagents%2Fsetup#0' + operationId: get-fleet-agents-setup parameters: - description: The version of the API to use in: header @@ -17748,7 +17482,7 @@ paths: - Elastic Agents post: description: Initiate agent setup - operationId: '%2Fapi%2Ffleet%2Fagents%2Fsetup#1' + operationId: post-fleet-agents-setup parameters: - description: The version of the API to use in: header @@ -17818,7 +17552,7 @@ paths: /api/fleet/agents/tags: get: description: List agent tags - operationId: '%2Fapi%2Ffleet%2Fagents%2Ftags#0' + operationId: get-fleet-agents-tags parameters: - description: The version of the API to use in: header @@ -17875,7 +17609,7 @@ paths: /api/fleet/check-permissions: get: description: Check permissions - operationId: '%2Fapi%2Ffleet%2Fcheck-permissions#0' + operationId: get-fleet-check-permissions parameters: - description: The version of the API to use in: header @@ -17930,7 +17664,7 @@ paths: /api/fleet/data_streams: get: description: List data streams - operationId: '%2Fapi%2Ffleet%2Fdata_streams#0' + operationId: get-fleet-data-streams parameters: - description: The version of the API to use in: header @@ -18035,7 +17769,7 @@ paths: /api/fleet/enrollment_api_keys: get: description: List enrollment API keys - operationId: '%2Fapi%2Ffleet%2Fenrollment_api_keys#0' + operationId: get-fleet-enrollment-api-keys parameters: - description: The version of the API to use in: header @@ -18178,7 +17912,7 @@ paths: - Fleet enrollment API keys post: description: Create enrollment API key - operationId: '%2Fapi%2Ffleet%2Fenrollment_api_keys#1' + operationId: post-fleet-enrollment-api-keys parameters: - description: The version of the API to use in: header @@ -18282,7 +18016,7 @@ paths: /api/fleet/enrollment_api_keys/{keyId}: delete: description: Revoke enrollment API key by ID by marking it as inactive - operationId: '%2Fapi%2Ffleet%2Fenrollment_api_keys%2F%7BkeyId%7D#1' + operationId: delete-fleet-enrollment-api-keys-keyid parameters: - description: The version of the API to use in: header @@ -18339,7 +18073,7 @@ paths: - Fleet enrollment API keys get: description: Get enrollment API key by ID - operationId: '%2Fapi%2Ffleet%2Fenrollment_api_keys%2F%7BkeyId%7D#0' + operationId: get-fleet-enrollment-api-keys-keyid parameters: - description: The version of the API to use in: header @@ -18420,7 +18154,7 @@ paths: - Fleet enrollment API keys /api/fleet/enrollment-api-keys: get: - operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys#0' + operationId: get-fleet-enrollment-api-keys-2 parameters: - description: The version of the API to use in: header @@ -18451,7 +18185,7 @@ paths: summary: '' tags: [] post: - operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys#1' + operationId: post-fleet-enrollment-api-keys-2 parameters: - description: The version of the API to use in: header @@ -18488,7 +18222,7 @@ paths: tags: [] /api/fleet/enrollment-api-keys/{keyId}: delete: - operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#1' + operationId: delete-fleet-enrollment-api-keys-keyid-2 parameters: - description: The version of the API to use in: header @@ -18514,7 +18248,7 @@ paths: summary: '' tags: [] get: - operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#0' + operationId: get-fleet-enrollment-api-keys-keyid-2 parameters: - description: The version of the API to use in: header @@ -18535,7 +18269,7 @@ paths: /api/fleet/epm/bulk_assets: post: description: Bulk get assets - operationId: '%2Fapi%2Ffleet%2Fepm%2Fbulk_assets#0' + operationId: post-fleet-epm-bulk-assets parameters: - description: The version of the API to use in: header @@ -18634,7 +18368,7 @@ paths: /api/fleet/epm/categories: get: description: List package categories - operationId: '%2Fapi%2Ffleet%2Fepm%2Fcategories#0' + operationId: get-fleet-epm-categories parameters: - description: The version of the API to use in: header @@ -18732,7 +18466,7 @@ paths: /api/fleet/epm/custom_integrations: post: description: Create custom integration - operationId: '%2Fapi%2Ffleet%2Fepm%2Fcustom_integrations#0' + operationId: post-fleet-epm-custom-integrations parameters: - description: The version of the API to use in: header @@ -18928,7 +18662,7 @@ paths: /api/fleet/epm/data_streams: get: description: List data streams - operationId: '%2Fapi%2Ffleet%2Fepm%2Fdata_streams#0' + operationId: get-fleet-epm-data-streams parameters: - description: The version of the API to use in: header @@ -19011,7 +18745,7 @@ paths: /api/fleet/epm/packages: get: description: List packages - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages#0' + operationId: get-fleet-epm-packages parameters: - description: The version of the API to use in: header @@ -19765,7 +19499,7 @@ paths: - Elastic Package Manager (EPM) post: description: Install package by upload - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages#1' + operationId: post-fleet-epm-packages parameters: - description: The version of the API to use in: header @@ -19946,7 +19680,7 @@ paths: /api/fleet/epm/packages/_bulk: post: description: Bulk install packages - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F_bulk#0' + operationId: post-fleet-epm-packages-bulk parameters: - description: The version of the API to use in: header @@ -20228,7 +19962,7 @@ paths: - Elastic Package Manager (EPM) /api/fleet/epm/packages/{pkgkey}: delete: - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#3' + operationId: delete-fleet-epm-packages-pkgkey parameters: - description: The version of the API to use in: header @@ -20266,7 +20000,7 @@ paths: summary: '' tags: [] get: - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#0' + operationId: get-fleet-epm-packages-pkgkey parameters: - description: The version of the API to use in: header @@ -20306,7 +20040,7 @@ paths: summary: '' tags: [] post: - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#2' + operationId: post-fleet-epm-packages-pkgkey parameters: - description: The version of the API to use in: header @@ -20361,7 +20095,7 @@ paths: summary: '' tags: [] put: - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#1' + operationId: put-fleet-epm-packages-pkgkey parameters: - description: The version of the API to use in: header @@ -20400,7 +20134,7 @@ paths: /api/fleet/epm/packages/{pkgName}/{pkgVersion}: delete: description: Delete package - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D#3' + operationId: delete-fleet-epm-packages-pkgname-pkgversion parameters: - description: The version of the API to use in: header @@ -20580,7 +20314,7 @@ paths: - Elastic Package Manager (EPM) get: description: Get package - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D#0' + operationId: get-fleet-epm-packages-pkgname-pkgversion parameters: - description: The version of the API to use in: header @@ -21465,7 +21199,7 @@ paths: - Elastic Package Manager (EPM) post: description: Install package from registry - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D#2' + operationId: post-fleet-epm-packages-pkgname-pkgversion parameters: - description: The version of the API to use in: header @@ -21668,7 +21402,7 @@ paths: - Elastic Package Manager (EPM) put: description: Update package settings - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D#1' + operationId: put-fleet-epm-packages-pkgname-pkgversion parameters: - description: The version of the API to use in: header @@ -22543,8 +22277,7 @@ paths: /api/fleet/epm/packages/{pkgName}/{pkgVersion}/{filePath*}: get: description: Get package file - operationId: >- - %2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D%2F%7BfilePath*%7D#0 + operationId: get-fleet-epm-packages-pkgname-pkgversion-filepath parameters: - description: The version of the API to use in: header @@ -22596,8 +22329,7 @@ paths: /api/fleet/epm/packages/{pkgName}/{pkgVersion}/transforms/authorize: post: description: Authorize transforms - operationId: >- - %2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D%2Ftransforms%2Fauthorize#0 + operationId: post-fleet-epm-packages-pkgname-pkgversion-transforms-authorize parameters: - description: The version of the API to use in: header @@ -22690,7 +22422,7 @@ paths: /api/fleet/epm/packages/{pkgName}/stats: get: description: Get package stats - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2Fstats#0' + operationId: get-fleet-epm-packages-pkgname-stats parameters: - description: The version of the API to use in: header @@ -22745,7 +22477,7 @@ paths: /api/fleet/epm/packages/installed: get: description: Get installed packages - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2Finstalled#0' + operationId: get-fleet-epm-packages-installed parameters: - description: The version of the API to use in: header @@ -22899,7 +22631,7 @@ paths: /api/fleet/epm/packages/limited: get: description: Get limited package list - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2Flimited#0' + operationId: get-fleet-epm-packages-limited parameters: - description: The version of the API to use in: header @@ -22950,8 +22682,7 @@ paths: /api/fleet/epm/templates/{pkgName}/{pkgVersion}/inputs: get: description: Get inputs template - operationId: >- - %2Fapi%2Ffleet%2Fepm%2Ftemplates%2F%7BpkgName%7D%2F%7BpkgVersion%7D%2Finputs#0 + operationId: get-fleet-epm-templates-pkgname-pkgversion-inputs parameters: - description: The version of the API to use in: header @@ -23059,7 +22790,7 @@ paths: /api/fleet/epm/verification_key_id: get: description: Get a package signature verification key ID - operationId: '%2Fapi%2Ffleet%2Fepm%2Fverification_key_id#0' + operationId: get-fleet-epm-verification-key-id parameters: - description: The version of the API to use in: header @@ -23104,7 +22835,7 @@ paths: /api/fleet/fleet_server_hosts: get: description: List Fleet Server hosts - operationId: '%2Fapi%2Ffleet%2Ffleet_server_hosts#0' + operationId: get-fleet-fleet-server-hosts parameters: - description: The version of the API to use in: header @@ -23184,7 +22915,7 @@ paths: - Fleet Server hosts post: description: Create Fleet Server host - operationId: '%2Fapi%2Ffleet%2Ffleet_server_hosts#1' + operationId: post-fleet-fleet-server-hosts parameters: - description: The version of the API to use in: header @@ -23291,7 +23022,7 @@ paths: /api/fleet/fleet_server_hosts/{itemId}: delete: description: Delete Fleet Server host by ID - operationId: '%2Fapi%2Ffleet%2Ffleet_server_hosts%2F%7BitemId%7D#1' + operationId: delete-fleet-fleet-server-hosts-itemid parameters: - description: The version of the API to use in: header @@ -23346,7 +23077,7 @@ paths: - Fleet Server hosts get: description: Get Fleet Server host by ID - operationId: '%2Fapi%2Ffleet%2Ffleet_server_hosts%2F%7BitemId%7D#0' + operationId: get-fleet-fleet-server-hosts-itemid parameters: - description: The version of the API to use in: header @@ -23420,7 +23151,7 @@ paths: - Fleet Server hosts put: description: Update Fleet Server host by ID - operationId: '%2Fapi%2Ffleet%2Ffleet_server_hosts%2F%7BitemId%7D#2' + operationId: put-fleet-fleet-server-hosts-itemid parameters: - description: The version of the API to use in: header @@ -23525,7 +23256,7 @@ paths: /api/fleet/health_check: post: description: Check Fleet Server health - operationId: '%2Fapi%2Ffleet%2Fhealth_check#0' + operationId: post-fleet-health-check parameters: - description: The version of the API to use in: header @@ -23613,7 +23344,7 @@ paths: /api/fleet/kubernetes: get: description: Get full K8s agent manifest - operationId: '%2Fapi%2Ffleet%2Fkubernetes#0' + operationId: get-fleet-kubernetes parameters: - description: The version of the API to use in: header @@ -23671,7 +23402,7 @@ paths: - Elastic Agent policies /api/fleet/kubernetes/download: get: - operationId: '%2Fapi%2Ffleet%2Fkubernetes%2Fdownload#0' + operationId: get-fleet-kubernetes-download parameters: - description: The version of the API to use in: header @@ -23740,7 +23471,7 @@ paths: /api/fleet/logstash_api_keys: post: description: Generate Logstash API key - operationId: '%2Fapi%2Ffleet%2Flogstash_api_keys#0' + operationId: post-fleet-logstash-api-keys parameters: - description: The version of the API to use in: header @@ -23791,7 +23522,7 @@ paths: /api/fleet/message_signing_service/rotate_key_pair: post: description: Rotate fleet message signing key pair - operationId: '%2Fapi%2Ffleet%2Fmessage_signing_service%2Frotate_key_pair#0' + operationId: post-fleet-message-signing-service-rotate-key-pair parameters: - description: The version of the API to use in: header @@ -23864,7 +23595,7 @@ paths: /api/fleet/outputs: get: description: List outputs - operationId: '%2Fapi%2Ffleet%2Foutputs#0' + operationId: get-fleet-outputs parameters: - description: The version of the API to use in: header @@ -24620,7 +24351,7 @@ paths: - Fleet outputs post: description: Create output - operationId: '%2Fapi%2Ffleet%2Foutputs#1' + operationId: post-fleet-outputs parameters: - description: The version of the API to use in: header @@ -26080,7 +25811,7 @@ paths: /api/fleet/outputs/{outputId}: delete: description: Delete output by ID - operationId: '%2Fapi%2Ffleet%2Foutputs%2F%7BoutputId%7D#2' + operationId: delete-fleet-outputs-outputid parameters: - description: The version of the API to use in: header @@ -26151,7 +25882,7 @@ paths: - Fleet outputs get: description: Get output by ID - operationId: '%2Fapi%2Ffleet%2Foutputs%2F%7BoutputId%7D#0' + operationId: get-fleet-outputs-outputid parameters: - description: The version of the API to use in: header @@ -26901,7 +26632,7 @@ paths: - Fleet outputs put: description: Update output by ID - operationId: '%2Fapi%2Ffleet%2Foutputs%2F%7BoutputId%7D#1' + operationId: put-fleet-outputs-outputid parameters: - description: The version of the API to use in: header @@ -28345,7 +28076,7 @@ paths: /api/fleet/outputs/{outputId}/health: get: description: Get latest output health - operationId: '%2Fapi%2Ffleet%2Foutputs%2F%7BoutputId%7D%2Fhealth#0' + operationId: get-fleet-outputs-outputid-health parameters: - description: The version of the API to use in: header @@ -28403,7 +28134,7 @@ paths: /api/fleet/package_policies: get: description: List package policies - operationId: '%2Fapi%2Ffleet%2Fpackage_policies#0' + operationId: get-fleet-package-policies parameters: - description: The version of the API to use in: header @@ -28908,7 +28639,7 @@ paths: - Fleet package policies post: description: Create package policy - operationId: '%2Fapi%2Ffleet%2Fpackage_policies#1' + operationId: post-fleet-package-policies parameters: - description: The version of the API to use in: header @@ -29811,7 +29542,7 @@ paths: /api/fleet/package_policies/_bulk_get: post: description: Bulk get package policies - operationId: '%2Fapi%2Ffleet%2Fpackage_policies%2F_bulk_get#0' + operationId: post-fleet-package-policies-bulk-get parameters: - description: The version of the API to use in: header @@ -30304,7 +30035,7 @@ paths: /api/fleet/package_policies/{packagePolicyId}: delete: description: Delete package policy by ID - operationId: '%2Fapi%2Ffleet%2Fpackage_policies%2F%7BpackagePolicyId%7D#2' + operationId: delete-fleet-package-policies-packagepolicyid parameters: - description: The version of the API to use in: header @@ -30364,7 +30095,7 @@ paths: - Fleet package policies get: description: Get package policy by ID - operationId: '%2Fapi%2Ffleet%2Fpackage_policies%2F%7BpackagePolicyId%7D#0' + operationId: get-fleet-package-policies-packagepolicyid parameters: - description: The version of the API to use in: header @@ -30833,7 +30564,7 @@ paths: - Fleet package policies put: description: Update package policy by ID - operationId: '%2Fapi%2Ffleet%2Fpackage_policies%2F%7BpackagePolicyId%7D#1' + operationId: put-fleet-package-policies-packagepolicyid parameters: - description: The version of the API to use in: header @@ -31730,7 +31461,7 @@ paths: /api/fleet/package_policies/delete: post: description: Bulk delete package policies - operationId: '%2Fapi%2Ffleet%2Fpackage_policies%2Fdelete#0' + operationId: post-fleet-package-policies-delete parameters: - description: The version of the API to use in: header @@ -31867,7 +31598,7 @@ paths: /api/fleet/package_policies/upgrade: post: description: Upgrade package policy to a newer package version - operationId: '%2Fapi%2Ffleet%2Fpackage_policies%2Fupgrade#0' + operationId: post-fleet-package-policies-upgrade parameters: - description: The version of the API to use in: header @@ -31948,7 +31679,7 @@ paths: /api/fleet/package_policies/upgrade/dryrun: post: description: Dry run package policy upgrade - operationId: '%2Fapi%2Ffleet%2Fpackage_policies%2Fupgrade%2Fdryrun#0' + operationId: post-fleet-package-policies-upgrade-dryrun parameters: - description: The version of the API to use in: header @@ -32798,7 +32529,7 @@ paths: /api/fleet/proxies: get: description: List proxies - operationId: '%2Fapi%2Ffleet%2Fproxies#0' + operationId: get-fleet-proxies parameters: - description: The version of the API to use in: header @@ -32884,7 +32615,7 @@ paths: - Fleet proxies post: description: Create proxy - operationId: '%2Fapi%2Ffleet%2Fproxies#1' + operationId: post-fleet-proxies parameters: - description: The version of the API to use in: header @@ -33003,7 +32734,7 @@ paths: /api/fleet/proxies/{itemId}: delete: description: Delete proxy by ID - operationId: '%2Fapi%2Ffleet%2Fproxies%2F%7BitemId%7D#2' + operationId: delete-fleet-proxies-itemid parameters: - description: The version of the API to use in: header @@ -33058,7 +32789,7 @@ paths: - Fleet proxies get: description: Get proxy by ID - operationId: '%2Fapi%2Ffleet%2Fproxies%2F%7BitemId%7D#1' + operationId: get-fleet-proxies-itemid parameters: - description: The version of the API to use in: header @@ -33138,7 +32869,7 @@ paths: - Fleet proxies put: description: Update proxy by ID - operationId: '%2Fapi%2Ffleet%2Fproxies%2F%7BitemId%7D#0' + operationId: put-fleet-proxies-itemid parameters: - description: The version of the API to use in: header @@ -33259,7 +32990,7 @@ paths: /api/fleet/service_tokens: post: description: Create a service token - operationId: '%2Fapi%2Ffleet%2Fservice_tokens#0' + operationId: post-fleet-service-tokens parameters: - description: The version of the API to use in: header @@ -33321,33 +33052,10 @@ paths: summary: '' tags: - Fleet service tokens - /api/fleet/service-tokens: - post: - description: Create a service token - operationId: '%2Fapi%2Ffleet%2Fservice-tokens#0' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - - description: A required header to protect against CSRF attacks - in: header - name: kbn-xsrf - required: true - schema: - example: 'true' - type: string - responses: {} - summary: '' - tags: [] /api/fleet/settings: get: description: Get settings - operationId: '%2Fapi%2Ffleet%2Fsettings#0' + operationId: get-fleet-settings parameters: - description: The version of the API to use in: header @@ -33446,7 +33154,7 @@ paths: - Fleet internals put: description: Update settings - operationId: '%2Fapi%2Ffleet%2Fsettings#1' + operationId: put-fleet-settings parameters: - description: The version of the API to use in: header @@ -33589,7 +33297,7 @@ paths: /api/fleet/setup: post: description: Initiate Fleet setup - operationId: '%2Fapi%2Ffleet%2Fsetup#0' + operationId: post-fleet-setup parameters: - description: The version of the API to use in: header @@ -33671,7 +33379,7 @@ paths: /api/fleet/uninstall_tokens: get: description: List metadata for latest uninstall tokens per agent policy - operationId: '%2Fapi%2Ffleet%2Funinstall_tokens#0' + operationId: get-fleet-uninstall-tokens parameters: - description: The version of the API to use in: header @@ -33771,7 +33479,7 @@ paths: /api/fleet/uninstall_tokens/{uninstallTokenId}: get: description: Get one decrypted uninstall token by its ID - operationId: '%2Fapi%2Ffleet%2Funinstall_tokens%2F%7BuninstallTokenId%7D#0' + operationId: get-fleet-uninstall-tokens-uninstalltokenid parameters: - description: The version of the API to use in: header @@ -36582,7 +36290,7 @@ paths: - Prompts API /api/security/role: get: - operationId: '%2Fapi%2Fsecurity%2Frole#0' + operationId: get-security-role parameters: - description: The version of the API to use in: header @@ -36609,7 +36317,7 @@ paths: - roles /api/security/role/{name}: delete: - operationId: '%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#1' + operationId: delete-security-role-name parameters: - description: The version of the API to use in: header @@ -36639,7 +36347,7 @@ paths: tags: - roles get: - operationId: '%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#0' + operationId: get-security-role-name parameters: - description: The version of the API to use in: header @@ -36675,7 +36383,7 @@ paths: description: >- Create a new Kibana role or update the attributes of an existing role. Kibana roles are stored in the Elasticsearch native realm. - operationId: '%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#2' + operationId: put-security-role-name parameters: - description: The version of the API to use in: header @@ -36958,7 +36666,7 @@ paths: - roles /api/security/roles: post: - operationId: '%2Fapi%2Fsecurity%2Froles#0' + operationId: post-security-roles parameters: - description: The version of the API to use in: header @@ -37242,7 +36950,7 @@ paths: - roles /api/spaces/space: get: - operationId: '%2Fapi%2Fspaces%2Fspace#0' + operationId: get-spaces-space parameters: - description: The version of the API to use in: header @@ -37298,7 +37006,7 @@ paths: tags: - spaces post: - operationId: '%2Fapi%2Fspaces%2Fspace#1' + operationId: post-spaces-space parameters: - description: The version of the API to use in: header @@ -37380,7 +37088,7 @@ paths: description: >- When you delete a space, all saved objects that belong to the space are automatically deleted, which is permanent and cannot be undone. - operationId: '%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#2' + operationId: delete-spaces-space-id parameters: - description: The version of the API to use in: header @@ -37412,7 +37120,7 @@ paths: tags: - spaces get: - operationId: '%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#0' + operationId: get-spaces-space-id parameters: - description: The version of the API to use in: header @@ -37435,7 +37143,7 @@ paths: tags: - spaces put: - operationId: '%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#1' + operationId: put-spaces-space-id parameters: - description: The version of the API to use in: header @@ -37522,7 +37230,7 @@ paths: - spaces /api/status: get: - operationId: '%2Fapi%2Fstatus#0' + operationId: get-status parameters: - description: The version of the API to use in: header @@ -40339,6 +40047,24 @@ components: title: Kibana Sample Data Logs type: index-pattern parameters: + APM_UI_elastic_api_version: + description: The version of the API to use + in: header + name: elastic-api-version + required: true + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + APM_UI_kbn_xsrf: + description: A required header to protect against CSRF attacks + in: header + name: kbn-xsrf + required: true + schema: + example: 'true' + type: string Data_views_field_name: description: The name of the runtime field. in: path @@ -40405,6 +40131,471 @@ components: example: default type: string schemas: + APM_UI_400_response: + type: object + properties: + error: + description: Error type + example: Not Found + type: string + message: + description: Error message + example: Not Found + type: string + statusCode: + description: Error status code + example: 400 + type: number + APM_UI_401_response: + type: object + properties: + error: + description: Error type + example: Unauthorized + type: string + message: + description: Error message + type: string + statusCode: + description: Error status code + example: 401 + type: number + APM_UI_403_response: + type: object + properties: + error: + description: Error type + example: Forbidden + type: string + message: + description: Error message + type: string + statusCode: + description: Error status code + example: 403 + type: number + APM_UI_404_response: + type: object + properties: + error: + description: Error type + example: Not Found + type: string + message: + description: Error message + example: Not Found + type: string + statusCode: + description: Error status code + example: 404 + type: number + APM_UI_500_response: + type: object + properties: + error: + description: Error type + example: Internal Server Error + type: string + message: + description: Error message + type: string + statusCode: + description: Error status code + example: 500 + type: number + APM_UI_501_response: + type: object + properties: + error: + description: Error type + example: Not Implemented + type: string + message: + description: Error message + example: Not Implemented + type: string + statusCode: + description: Error status code + example: 501 + type: number + APM_UI_agent_configuration_intake_object: + type: object + properties: + agent_name: + description: Agent name + type: string + service: + $ref: '#/components/schemas/APM_UI_service_object' + settings: + $ref: '#/components/schemas/APM_UI_settings_object' + required: + - service + - settings + APM_UI_agent_configuration_object: + description: Agent configuration + type: object + properties: + '@timestamp': + description: Timestamp + example: 1730194190636 + type: number + agent_name: + description: Agent name + type: string + applied_by_agent: + description: Applied by agent + example: true + type: boolean + etag: + description: Etag + example: 0bc3b5ebf18fba8163fe4c96f491e3767a358f85 + type: string + service: + $ref: '#/components/schemas/APM_UI_service_object' + settings: + $ref: '#/components/schemas/APM_UI_settings_object' + required: + - service + - settings + - '@timestamp' + - etag + APM_UI_agent_configurations_response: + type: object + properties: + configurations: + description: Agent configuration + items: + $ref: '#/components/schemas/APM_UI_agent_configuration_object' + type: array + APM_UI_agent_keys_object: + type: object + properties: + name: + description: Agent name + type: string + privileges: + description: Privileges configuration + items: + enum: + - event:write + - config_agent:read + type: string + type: array + required: + - name + - privileges + APM_UI_agent_keys_response: + type: object + properties: + agentKey: + description: Agent key + type: object + properties: + api_key: + type: string + encoded: + type: string + expiration: + format: int64 + type: integer + id: + type: string + name: + type: string + required: + - id + - name + - api_key + - encoded + APM_UI_annotation_search_response: + type: object + properties: + annotations: + description: Annotations + items: + type: object + properties: + '@timestamp': + type: number + id: + type: string + text: + type: string + type: + enum: + - version + type: string + type: array + APM_UI_base_source_map_object: + type: object + properties: + compressionAlgorithm: + description: Compression Algorithm + type: string + created: + description: Created date + type: string + decodedSha256: + description: Decoded SHA-256 + type: string + decodedSize: + description: Decoded size + type: number + encodedSha256: + description: Encoded SHA-256 + type: string + encodedSize: + description: Encoded size + type: number + encryptionAlgorithm: + description: Encryption Algorithm + type: string + id: + description: Identifier + type: string + identifier: + description: Identifier + type: string + packageName: + description: Package name + type: string + relative_url: + description: Relative URL + type: string + type: + description: Type + type: string + APM_UI_create_annotation_object: + type: object + properties: + '@timestamp': + description: Timestamp + type: string + message: + description: Message + type: string + service: + description: Service + type: object + properties: + environment: + type: string + version: + type: string + required: + - version + tags: + description: Tags + items: + type: string + type: array + required: + - '@timestamp' + - service + APM_UI_create_annotation_response: + type: object + properties: + _id: + description: Identifier + type: string + _index: + description: Index + type: string + _source: + description: Response + type: object + properties: + '@timestamp': + type: string + annotation: + type: object + properties: + title: + type: string + type: + type: string + event: + type: object + properties: + created: + type: string + message: + type: string + service: + type: object + properties: + environment: + type: string + name: + type: string + version: + type: string + tags: + items: + type: string + type: array + APM_UI_delete_agent_configurations_response: + type: object + properties: + result: + description: Result + type: string + APM_UI_search_agent_configuration_object: + type: object + properties: + etag: + description: If etags match then `applied_by_agent` field will be set to `true` + example: 0bc3b5ebf18fba8163fe4c96f491e3767a358f85 + type: string + mark_as_applied_by_agent: + description: > + `markAsAppliedByAgent=true` means "force setting it to true + regardless of etag". + + This is needed for Jaeger agent that doesn't have etags + type: boolean + service: + $ref: '#/components/schemas/APM_UI_service_object' + required: + - service + APM_UI_search_agent_configuration_response: + type: object + properties: + _id: + description: Identifier + type: string + _index: + description: Index + type: string + _score: + description: Score + type: number + _source: + $ref: '#/components/schemas/APM_UI_agent_configuration_object' + APM_UI_service_agent_name_response: + type: object + properties: + agentName: + description: Agent name + example: nodejs + type: string + APM_UI_service_environment_object: + type: object + properties: + alreadyConfigured: + description: Already configured + type: boolean + name: + description: Service environment name + example: ALL_OPTION_VALUE + type: string + APM_UI_service_environments_response: + type: object + properties: + environments: + description: Service environment list + items: + $ref: '#/components/schemas/APM_UI_service_environment_object' + type: array + APM_UI_service_object: + description: Service + type: object + properties: + environment: + description: Environment + example: prod + type: string + name: + description: Name + example: node + type: string + APM_UI_settings_object: + additionalProperties: + type: string + description: Agent configuration settings + type: object + APM_UI_single_agent_configuration_response: + allOf: + - type: object + properties: + id: + type: string + required: + - id + - $ref: '#/components/schemas/APM_UI_agent_configuration_object' + APM_UI_source_maps_response: + type: object + properties: + artifacts: + description: Artifacts + items: + allOf: + - type: object + properties: + body: + type: object + properties: + bundleFilepath: + type: string + serviceName: + type: string + serviceVersion: + type: string + sourceMap: + type: object + properties: + file: + type: string + mappings: + type: string + sourceRoot: + type: string + sources: + items: + type: string + type: array + sourcesContent: + items: + type: string + type: array + version: + type: number + - $ref: '#/components/schemas/APM_UI_base_source_map_object' + type: array + APM_UI_upload_source_map_object: + type: object + properties: + bundle_filepath: + description: >- + The absolute path of the final bundle as used in the web + application. + type: string + service_name: + description: The name of the service that the service map should apply to. + type: string + service_version: + description: The version of the service that the service map should apply to. + type: string + sourcemap: + description: > + The source map. String or file upload. It must follow the + + [source map revision 3 + proposal](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k). + format: binary + type: string + required: + - service_name + - service_version + - bundle_filepath + - sourcemap + APM_UI_upload_source_maps_response: + allOf: + - type: object + properties: + body: + type: string + - $ref: '#/components/schemas/APM_UI_base_source_map_object' Data_views_400_response: title: Bad request type: object @@ -48753,6 +48944,8 @@ components: Security_Entity_Analytics_API_EngineDescriptor: type: object properties: + error: + type: object fieldHistoryLength: type: integer filter: @@ -52152,6 +52345,9 @@ security: - apiKeyAuth: [] tags: - name: alerting + - description: | + Adjust APM agent configuration without need to redeploy your application. + name: APM agent configuration - description: > Configure APM agent keys to authorize requests from APM agents to the APM Server. @@ -52161,6 +52357,10 @@ tags: Annotations enable you to easily see how events are impacting the performance of your applications. name: APM annotations + - description: Create APM fleet server schema. + name: APM server schema + - description: Configure APM source maps. + name: APM sourcemaps - name: connectors - name: Data streams - description: >- diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml index 930d70cbd27b0..6122607df925f 100644 --- a/oas_docs/output/kibana.yaml +++ b/oas_docs/output/kibana.yaml @@ -79,396 +79,10 @@ servers: kibana_url: default: localhost:5601 paths: - /api/actions: - get: - deprecated: true - operationId: '%2Fapi%2Factions#0' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - responses: {} - summary: Get all connectors - tags: - - connectors - /api/actions/action: - post: - deprecated: true - operationId: '%2Fapi%2Factions%2Faction#0' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - - description: A required header to protect against CSRF attacks - in: header - name: kbn-xsrf - required: true - schema: - example: 'true' - type: string - requestBody: - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - additionalProperties: false - type: object - properties: - actionTypeId: - description: The connector type identifier. - type: string - config: - additionalProperties: {} - default: {} - type: object - name: - description: The display name for the connector. - type: string - secrets: - additionalProperties: {} - default: {} - type: object - required: - - name - - actionTypeId - responses: - '200': - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - additionalProperties: false - type: object - properties: - config: - additionalProperties: {} - type: object - connector_type_id: - description: The connector type identifier. - type: string - id: - description: The identifier for the connector. - type: string - is_deprecated: - description: Indicates whether the connector is deprecated. - type: boolean - is_missing_secrets: - description: Indicates whether the connector is missing secrets. - type: boolean - is_preconfigured: - description: >- - Indicates whether the connector is preconfigured. If true, - the `config` and `is_missing_secrets` properties are - omitted from the response. - type: boolean - is_system_action: - description: >- - Indicates whether the connector is used for system - actions. - type: boolean - name: - description: ' The name of the rule.' - type: string - required: - - id - - name - - connector_type_id - - is_preconfigured - - is_deprecated - - is_system_action - description: Indicates a successful call. - summary: Create a connector - tags: - - connectors - /api/actions/action/{id}: - delete: - deprecated: true - description: 'WARNING: When you delete a connector, it cannot be recovered.' - operationId: '%2Fapi%2Factions%2Faction%2F%7Bid%7D#0' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - - description: A required header to protect against CSRF attacks - in: header - name: kbn-xsrf - required: true - schema: - example: 'true' - type: string - - description: An identifier for the connector. - in: path - name: id - required: true - schema: - type: string - responses: - '204': - description: Indicates a successful call. - summary: Delete a connector - tags: - - connectors - get: - deprecated: true - operationId: '%2Fapi%2Factions%2Faction%2F%7Bid%7D#1' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - - description: An identifier for the connector. - in: path - name: id - required: true - schema: - type: string - responses: - '200': - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - additionalProperties: false - type: object - properties: - config: - additionalProperties: {} - type: object - connector_type_id: - description: The connector type identifier. - type: string - id: - description: The identifier for the connector. - type: string - is_deprecated: - description: Indicates whether the connector is deprecated. - type: boolean - is_missing_secrets: - description: Indicates whether the connector is missing secrets. - type: boolean - is_preconfigured: - description: >- - Indicates whether the connector is preconfigured. If true, - the `config` and `is_missing_secrets` properties are - omitted from the response. - type: boolean - is_system_action: - description: >- - Indicates whether the connector is used for system - actions. - type: boolean - name: - description: ' The name of the rule.' - type: string - required: - - id - - name - - connector_type_id - - is_preconfigured - - is_deprecated - - is_system_action - description: Indicates a successful call. - summary: Get connector information - tags: - - connectors - put: - deprecated: true - operationId: '%2Fapi%2Factions%2Faction%2F%7Bid%7D#2' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - - description: A required header to protect against CSRF attacks - in: header - name: kbn-xsrf - required: true - schema: - example: 'true' - type: string - - description: An identifier for the connector. - in: path - name: id - required: true - schema: - type: string - requestBody: - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - additionalProperties: false - type: object - properties: - config: - additionalProperties: {} - default: {} - type: object - name: - type: string - secrets: - additionalProperties: {} - default: {} - type: object - required: - - name - responses: - '200': - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - additionalProperties: false - type: object - properties: - config: - additionalProperties: {} - type: object - connector_type_id: - description: The connector type identifier. - type: string - id: - description: The identifier for the connector. - type: string - is_deprecated: - description: Indicates whether the connector is deprecated. - type: boolean - is_missing_secrets: - description: Indicates whether the connector is missing secrets. - type: boolean - is_preconfigured: - description: >- - Indicates whether the connector is preconfigured. If true, - the `config` and `is_missing_secrets` properties are - omitted from the response. - type: boolean - is_system_action: - description: >- - Indicates whether the connector is used for system - actions. - type: boolean - name: - description: ' The name of the rule.' - type: string - required: - - id - - name - - connector_type_id - - is_preconfigured - - is_deprecated - - is_system_action - description: Indicates a successful call. - summary: Update a connector - tags: - - connectors - /api/actions/action/{id}/_execute: - post: - deprecated: true - operationId: '%2Fapi%2Factions%2Faction%2F%7Bid%7D%2F_execute#0' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - - description: A required header to protect against CSRF attacks - in: header - name: kbn-xsrf - required: true - schema: - example: 'true' - type: string - - description: An identifier for the connector. - in: path - name: id - required: true - schema: - type: string - requestBody: - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - additionalProperties: false - type: object - properties: - params: - additionalProperties: {} - type: object - required: - - params - responses: - '200': - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - additionalProperties: false - type: object - properties: - config: - additionalProperties: {} - type: object - connector_type_id: - description: The connector type identifier. - type: string - id: - description: The identifier for the connector. - type: string - is_deprecated: - description: Indicates whether the connector is deprecated. - type: boolean - is_missing_secrets: - description: Indicates whether the connector is missing secrets. - type: boolean - is_preconfigured: - description: >- - Indicates whether the connector is preconfigured. If true, - the `config` and `is_missing_secrets` properties are - omitted from the response. - type: boolean - is_system_action: - description: >- - Indicates whether the connector is used for system - actions. - type: boolean - name: - description: ' The name of the rule.' - type: string - required: - - id - - name - - connector_type_id - - is_preconfigured - - is_deprecated - - is_system_action - description: Indicates a successful call. - summary: Run a connector - tags: - - connectors /api/actions/connector_types: get: description: You do not need any Kibana feature privileges to run this API. - operationId: '%2Fapi%2Factions%2Fconnector_types#0' + operationId: get-actions-connector-types parameters: - description: The version of the API to use in: header @@ -493,7 +107,7 @@ paths: /api/actions/connector/{id}: delete: description: 'WARNING: When you delete a connector, it cannot be recovered.' - operationId: '%2Fapi%2Factions%2Fconnector%2F%7Bid%7D#0' + operationId: delete-actions-connector-id parameters: - description: The version of the API to use in: header @@ -523,7 +137,7 @@ paths: tags: - connectors get: - operationId: '%2Fapi%2Factions%2Fconnector%2F%7Bid%7D#1' + operationId: get-actions-connector-id parameters: - description: The version of the API to use in: header @@ -588,7 +202,7 @@ paths: tags: - connectors post: - operationId: '%2Fapi%2Factions%2Fconnector%2F%7Bid%3F%7D#0' + operationId: post-actions-connector-id parameters: - description: The version of the API to use in: header @@ -684,7 +298,7 @@ paths: tags: - connectors put: - operationId: '%2Fapi%2Factions%2Fconnector%2F%7Bid%7D#2' + operationId: put-actions-connector-id parameters: - description: The version of the API to use in: header @@ -780,7 +394,7 @@ paths: description: >- You can use this API to test an action that involves interaction with Kibana services or integrations with third-party systems. - operationId: '%2Fapi%2Factions%2Fconnector%2F%7Bid%7D%2F_execute#0' + operationId: post-actions-connector-id-execute parameters: - description: The version of the API to use in: header @@ -865,7 +479,7 @@ paths: - connectors /api/actions/connectors: get: - operationId: '%2Fapi%2Factions%2Fconnectors#0' + operationId: get-actions-connectors parameters: - description: The version of the API to use in: header @@ -879,23 +493,6 @@ paths: summary: Get all connectors tags: - connectors - /api/actions/list_action_types: - get: - deprecated: true - operationId: '%2Fapi%2Factions%2Flist_action_types#0' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - responses: {} - summary: Get connector types - tags: - - connectors /api/alerting/_health: get: description: > @@ -1286,7 +883,7 @@ paths: - alerting /api/alerting/rule/{id}: delete: - operationId: '%2Fapi%2Falerting%2Frule%2F%7Bid%7D#2' + operationId: delete-alerting-rule-id parameters: - description: The version of the API to use in: header @@ -1322,7 +919,7 @@ paths: tags: - alerting get: - operationId: '%2Fapi%2Falerting%2Frule%2F%7Bid%7D#0' + operationId: get-alerting-rule-id parameters: - description: The version of the API to use in: header @@ -2120,7 +1717,7 @@ paths: tags: - alerting post: - operationId: '%2Fapi%2Falerting%2Frule%2F%7Bid%3F%7D#0' + operationId: post-alerting-rule-id parameters: - description: The version of the API to use in: header @@ -3243,7 +2840,7 @@ paths: tags: - alerting put: - operationId: '%2Fapi%2Falerting%2Frule%2F%7Bid%7D#1' + operationId: put-alerting-rule-id parameters: - description: The version of the API to use in: header @@ -4340,7 +3937,7 @@ paths: - alerting /api/alerting/rule/{id}/_disable: post: - operationId: '%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_disable#0' + operationId: post-alerting-rule-id-disable parameters: - description: The version of the API to use in: header @@ -4389,7 +3986,7 @@ paths: - alerting /api/alerting/rule/{id}/_enable: post: - operationId: '%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_enable#0' + operationId: post-alerting-rule-id-enable parameters: - description: The version of the API to use in: header @@ -4426,7 +4023,7 @@ paths: - alerting /api/alerting/rule/{id}/_mute_all: post: - operationId: '%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_mute_all#0' + operationId: post-alerting-rule-id-mute-all parameters: - description: The version of the API to use in: header @@ -4463,7 +4060,7 @@ paths: - alerting /api/alerting/rule/{id}/_unmute_all: post: - operationId: '%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_unmute_all#0' + operationId: post-alerting-rule-id-unmute-all parameters: - description: The version of the API to use in: header @@ -4500,7 +4097,7 @@ paths: - alerting /api/alerting/rule/{id}/_update_api_key: post: - operationId: '%2Fapi%2Falerting%2Frule%2F%7Bid%7D%2F_update_api_key#0' + operationId: post-alerting-rule-id-update-api-key parameters: - description: The version of the API to use in: header @@ -4539,8 +4136,7 @@ paths: - alerting /api/alerting/rule/{rule_id}/alert/{alert_id}/_mute: post: - operationId: >- - %2Fapi%2Falerting%2Frule%2F%7Brule_id%7D%2Falert%2F%7Balert_id%7D%2F_mute#0 + operationId: post-alerting-rule-rule-id-alert-alert-id-mute parameters: - description: The version of the API to use in: header @@ -4583,8 +4179,7 @@ paths: - alerting /api/alerting/rule/{rule_id}/alert/{alert_id}/_unmute: post: - operationId: >- - %2Fapi%2Falerting%2Frule%2F%7Brule_id%7D%2Falert%2F%7Balert_id%7D%2F_unmute#0 + operationId: post-alerting-rule-rule-id-alert-alert-id-unmute parameters: - description: The version of the API to use in: header @@ -4627,7 +4222,7 @@ paths: - alerting /api/alerting/rules/_find: get: - operationId: '%2Fapi%2Falerting%2Frules%2F_find#0' + operationId: get-alerting-rules-find parameters: - description: The version of the API to use in: header @@ -6322,49 +5917,110 @@ paths: post: description: Create a new agent key for APM. operationId: createAgentKey + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - $ref: '#/components/parameters/APM_UI_kbn_xsrf' requestBody: content: application/json; Elastic-Api-Version=2023-10-31: schema: - type: object - properties: - name: - type: string - privileges: - items: - enum: - - event:write - - config_agent:read - type: string - type: array + $ref: '#/components/schemas/APM_UI_agent_keys_object' required: true responses: '200': content: application/json; Elastic-Api-Version=2023-10-31: schema: - type: object - properties: - api_key: - type: string - encoded: - type: string - expiration: - format: int64 - type: integer - id: - type: string - name: - type: string + $ref: '#/components/schemas/APM_UI_agent_keys_response' description: Agent key created successfully + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '403': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_403_response' + description: Forbidden response + '500': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_500_response' + description: Internal Server Error response summary: Create an APM agent key tags: - APM agent keys + /api/apm/fleet/apm_server_schema: + post: + operationId: saveApmServerSchema + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - $ref: '#/components/parameters/APM_UI_kbn_xsrf' + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + type: object + properties: + schema: + additionalProperties: true + description: Schema object + example: + foo: bar + type: object + required: true + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '403': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_403_response' + description: Forbidden response + '404': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_404_response' + description: Not found response + summary: Save APM server schema + tags: + - APM server schema /api/apm/services/{serviceName}/annotation: post: description: Create a new annotation for a specific service. operationId: createAnnotation parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - $ref: '#/components/parameters/APM_UI_kbn_xsrf' - description: The name of the service in: path name: serviceName @@ -6375,63 +6031,39 @@ paths: content: application/json; Elastic-Api-Version=2023-10-31: schema: - type: object - properties: - '@timestamp': - type: string - message: - type: string - service: - type: object - properties: - environment: - type: string - version: - type: string - tags: - items: - type: string - type: array + $ref: '#/components/schemas/APM_UI_create_annotation_object' required: true responses: '200': content: application/json; Elastic-Api-Version=2023-10-31: schema: - type: object - properties: - _id: - type: string - _index: - type: string - _source: - type: object - properties: - '@timestamp': - type: string - annotation: - type: string - event: - type: object - properties: - created: - type: string - message: - type: string - service: - type: object - properties: - environment: - type: string - name: - type: string - version: - type: string - tags: - items: - type: string - type: array + $ref: '#/components/schemas/APM_UI_create_annotation_response' description: Annotation created successfully + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '403': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_403_response' + description: Forbidden response + '404': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_404_response' + description: Not found response summary: Create a service annotation tags: - APM annotations @@ -6440,6 +6072,7 @@ paths: description: Search for annotations related to a specific service. operationId: getAnnotation parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' - description: The name of the service in: path name: serviceName @@ -6469,27 +6102,484 @@ paths: content: application/json; Elastic-Api-Version=2023-10-31: schema: - type: object - properties: - annotations: - items: - type: object - properties: - '@timestamp': - type: number - id: - type: string - text: - type: string - type: - enum: - - version - type: string - type: array + $ref: '#/components/schemas/APM_UI_annotation_search_response' description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '500': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_500_response' + description: Internal Server Error response summary: Search for annotations tags: - APM annotations + /api/apm/settings/agent-configuration: + delete: + operationId: deleteAgentConfiguration + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - $ref: '#/components/parameters/APM_UI_kbn_xsrf' + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_service_object' + required: true + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: >- + #/components/schemas/APM_UI_delete_agent_configurations_response + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '403': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_403_response' + description: Forbidden response + '404': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_404_response' + description: Not found response + summary: Delete agent configuration + tags: + - APM agent configuration + get: + operationId: getAgentConfigurations + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_agent_configurations_response' + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '404': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_404_response' + description: Not found response + summary: Get a list of agent configurations + tags: + - APM agent configuration + put: + operationId: createUpdateAgentConfiguration + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - $ref: '#/components/parameters/APM_UI_kbn_xsrf' + - description: If the config exists ?overwrite=true is required + in: query + name: overwrite + schema: + type: boolean + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_agent_configuration_intake_object' + required: true + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '403': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_403_response' + description: Forbidden response + '404': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_404_response' + description: Not found response + summary: Create or update agent configuration + tags: + - APM agent configuration + /api/apm/settings/agent-configuration/agent_name: + get: + description: Retrieve `agentName` for a service. + operationId: getAgentNameForService + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - description: The name of the service + example: node + in: query + name: serviceName + required: true + schema: + type: string + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_service_agent_name_response' + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '404': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_404_response' + description: Not found response + summary: Get agent name for service + tags: + - APM agent configuration + /api/apm/settings/agent-configuration/environments: + get: + operationId: getEnvironmentsForService + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - description: The name of the service + in: query + name: serviceName + schema: + type: string + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_service_environments_response' + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '404': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_404_response' + description: Not found response + summary: Get environments for service + tags: + - APM agent configuration + /api/apm/settings/agent-configuration/search: + post: + description: > + This endpoint allows to search for single agent configuration and update + 'applied_by_agent' field. + operationId: searchSingleConfiguration + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - $ref: '#/components/parameters/APM_UI_kbn_xsrf' + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_search_agent_configuration_object' + required: true + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: >- + #/components/schemas/APM_UI_search_agent_configuration_response + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '404': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_404_response' + description: Not found response + summary: Lookup single agent configuration + tags: + - APM agent configuration + /api/apm/settings/agent-configuration/view: + get: + operationId: getSingleAgentConfiguration + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - description: Service name + example: node + in: query + name: name + schema: + type: string + - description: Service environment + example: prod + in: query + name: environment + schema: + type: string + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: >- + #/components/schemas/APM_UI_single_agent_configuration_response + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '404': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_404_response' + description: Not found response + summary: Get single agent configuration + tags: + - APM agent configuration + /api/apm/sourcemaps: + get: + description: Returns an array of Fleet artifacts, including source map uploads. + operationId: getSourceMaps + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - description: Page number + in: query + name: page + schema: + type: number + - description: Number of records per page + in: query + name: perPage + schema: + type: number + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_source_maps_response' + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '500': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_500_response' + description: Internal Server Error response + '501': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_501_response' + description: Not Implemented response + summary: Get source maps + tags: + - APM sourcemaps + post: + description: Upload a source map for a specific service and version. + operationId: uploadSourceMap + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - $ref: '#/components/parameters/APM_UI_kbn_xsrf' + requestBody: + content: + multipart/form-data; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_upload_source_map_object' + required: true + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_upload_source_maps_response' + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '403': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_403_response' + description: Forbidden response + '500': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_500_response' + description: Internal Server Error response + '501': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_501_response' + description: Not Implemented response + summary: Upload source map + tags: + - APM sourcemaps + /api/apm/sourcemaps/{id}: + delete: + description: Delete a previously uploaded source map. + operationId: deleteSourceMap + parameters: + - $ref: '#/components/parameters/APM_UI_elastic_api_version' + - $ref: '#/components/parameters/APM_UI_kbn_xsrf' + - description: Source map identifier + in: path + name: id + required: true + schema: + type: string + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_400_response' + description: Bad Request response + '401': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_401_response' + description: Unauthorized response + '403': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_403_response' + description: Forbidden response + '500': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_500_response' + description: Internal Server Error response + '501': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: '#/components/schemas/APM_UI_501_response' + description: Not Implemented response + summary: Delete source map + tags: + - APM sourcemaps /api/asset_criticality: delete: description: Delete the asset criticality record for a specific entity. @@ -13214,7 +13304,7 @@ paths: /api/fleet/agent_download_sources: get: description: List agent binary download sources - operationId: '%2Fapi%2Ffleet%2Fagent_download_sources#0' + operationId: get-fleet-agent-download-sources parameters: - description: The version of the API to use in: header @@ -13290,7 +13380,7 @@ paths: - Elastic Agent binary download sources post: description: Create agent binary download source - operationId: '%2Fapi%2Ffleet%2Fagent_download_sources#1' + operationId: post-fleet-agent-download-sources parameters: - description: The version of the API to use in: header @@ -13389,7 +13479,7 @@ paths: /api/fleet/agent_download_sources/{sourceId}: delete: description: Delete agent binary download source by ID - operationId: '%2Fapi%2Ffleet%2Fagent_download_sources%2F%7BsourceId%7D#2' + operationId: delete-fleet-agent-download-sources-sourceid parameters: - description: The version of the API to use in: header @@ -13444,7 +13534,7 @@ paths: - Elastic Agent binary download sources get: description: Get agent binary download source by ID - operationId: '%2Fapi%2Ffleet%2Fagent_download_sources%2F%7BsourceId%7D#0' + operationId: get-fleet-agent-download-sources-sourceid parameters: - description: The version of the API to use in: header @@ -13514,7 +13604,7 @@ paths: - Elastic Agent binary download sources put: description: Update agent binary download source by ID - operationId: '%2Fapi%2Ffleet%2Fagent_download_sources%2F%7BsourceId%7D#1' + operationId: put-fleet-agent-download-sources-sourceid parameters: - description: The version of the API to use in: header @@ -13618,7 +13708,7 @@ paths: /api/fleet/agent_policies: get: description: List agent policies - operationId: '%2Fapi%2Ffleet%2Fagent_policies#0' + operationId: get-fleet-agent-policies parameters: - description: The version of the API to use in: header @@ -14232,7 +14322,7 @@ paths: - Elastic Agent policies post: description: Create an agent policy - operationId: '%2Fapi%2Ffleet%2Fagent_policies#1' + operationId: post-fleet-agent-policies parameters: - description: The version of the API to use in: header @@ -14967,7 +15057,7 @@ paths: /api/fleet/agent_policies/_bulk_get: post: description: Bulk get agent policies - operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F_bulk_get#0' + operationId: post-fleet-agent-policies-bulk-get parameters: - description: The version of the API to use in: header @@ -15547,7 +15637,7 @@ paths: /api/fleet/agent_policies/{agentPolicyId}: get: description: Get an agent policy by ID - operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D#0' + operationId: get-fleet-agent-policies-agentpolicyid parameters: - description: The version of the API to use in: header @@ -16103,7 +16193,7 @@ paths: - Elastic Agent policies put: description: Update an agent policy by ID - operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D#1' + operationId: put-fleet-agent-policies-agentpolicyid parameters: - description: The version of the API to use in: header @@ -16846,7 +16936,7 @@ paths: /api/fleet/agent_policies/{agentPolicyId}/copy: post: description: Copy an agent policy by ID - operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Fcopy#0' + operationId: post-fleet-agent-policies-agentpolicyid-copy parameters: - description: The version of the API to use in: header @@ -17424,7 +17514,7 @@ paths: /api/fleet/agent_policies/{agentPolicyId}/download: get: description: Download an agent policy by ID - operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Fdownload#0' + operationId: get-fleet-agent-policies-agentpolicyid-download parameters: - description: The version of the API to use in: header @@ -17498,7 +17588,7 @@ paths: /api/fleet/agent_policies/{agentPolicyId}/full: get: description: Get a full agent policy by ID - operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Ffull#0' + operationId: get-fleet-agent-policies-agentpolicyid-full parameters: - description: The version of the API to use in: header @@ -17830,7 +17920,7 @@ paths: /api/fleet/agent_policies/{agentPolicyId}/outputs: get: description: Get list of outputs associated with agent policy by policy id - operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Foutputs#0' + operationId: get-fleet-agent-policies-agentpolicyid-outputs parameters: - description: The version of the API to use in: header @@ -17934,7 +18024,7 @@ paths: /api/fleet/agent_policies/delete: post: description: Delete agent policy by ID - operationId: '%2Fapi%2Ffleet%2Fagent_policies%2Fdelete#0' + operationId: post-fleet-agent-policies-delete parameters: - description: The version of the API to use in: header @@ -18004,7 +18094,7 @@ paths: /api/fleet/agent_policies/outputs: post: description: Get list of outputs associated with agent policies - operationId: '%2Fapi%2Ffleet%2Fagent_policies%2Foutputs#0' + operationId: post-fleet-agent-policies-outputs parameters: - description: The version of the API to use in: header @@ -18126,7 +18216,7 @@ paths: /api/fleet/agent_status: get: description: Get agent status summary - operationId: '%2Fapi%2Ffleet%2Fagent_status#0' + operationId: get-fleet-agent-status parameters: - description: The version of the API to use in: header @@ -18154,7 +18244,6 @@ paths: name: kuery required: false schema: - deprecated: true type: string responses: '200': @@ -18184,16 +18273,12 @@ paths: type: number other: type: number - total: - deprecated: true - type: number unenrolled: type: number updating: type: number required: - events - - total - online - error - offline @@ -18227,7 +18312,7 @@ paths: /api/fleet/agent_status/data: get: description: Get incoming agent data - operationId: '%2Fapi%2Ffleet%2Fagent_status%2Fdata#0' + operationId: get-fleet-agent-status-data parameters: - description: The version of the API to use in: header @@ -18297,45 +18382,10 @@ paths: summary: '' tags: - Elastic Agents - /api/fleet/agent-status: - get: - operationId: '%2Fapi%2Ffleet%2Fagent-status#0' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - - in: query - name: policyId - required: false - schema: - type: string - - in: query - name: policyIds - required: false - schema: - anyOf: - - items: - type: string - type: array - - type: string - - in: query - name: kuery - required: false - schema: - deprecated: true - type: string - responses: {} - summary: '' - tags: [] /api/fleet/agents: get: description: List agents - operationId: '%2Fapi%2Ffleet%2Fagents#0' + operationId: get-fleet-agents parameters: - description: The version of the API to use in: header @@ -18685,285 +18735,6 @@ paths: - enrolled_at - local_metadata type: array - list: - deprecated: true - items: - additionalProperties: false - type: object - properties: - access_api_key: - type: string - access_api_key_id: - type: string - active: - type: boolean - agent: - additionalProperties: true - type: object - properties: - id: - type: string - version: - type: string - required: - - id - - version - components: - items: - additionalProperties: false - type: object - properties: - id: - type: string - message: - type: string - status: - enum: - - STARTING - - CONFIGURING - - HEALTHY - - DEGRADED - - FAILED - - STOPPING - - STOPPED - type: string - type: - type: string - units: - items: - additionalProperties: false - type: object - properties: - id: - type: string - message: - type: string - payload: - additionalProperties: {} - type: object - status: - enum: - - STARTING - - CONFIGURING - - HEALTHY - - DEGRADED - - FAILED - - STOPPING - - STOPPED - type: string - type: - enum: - - input - - output - type: string - required: - - id - - type - - status - - message - type: array - required: - - id - - type - - status - - message - type: array - default_api_key: - type: string - default_api_key_history: - items: - additionalProperties: false - deprecated: true - type: object - properties: - id: - type: string - retired_at: - type: string - required: - - id - - retired_at - type: array - default_api_key_id: - type: string - enrolled_at: - type: string - id: - type: string - last_checkin: - type: string - last_checkin_message: - type: string - last_checkin_status: - enum: - - error - - online - - degraded - - updating - - starting - type: string - local_metadata: - additionalProperties: {} - type: object - metrics: - additionalProperties: false - type: object - properties: - cpu_avg: - type: number - memory_size_byte_avg: - type: number - namespaces: - items: - type: string - type: array - outputs: - additionalProperties: - additionalProperties: false - type: object - properties: - api_key_id: - type: string - to_retire_api_key_ids: - items: - additionalProperties: false - type: object - properties: - id: - type: string - retired_at: - type: string - required: - - id - - retired_at - type: array - type: - type: string - required: - - api_key_id - - type - type: object - packages: - items: - type: string - type: array - policy_id: - type: string - policy_revision: - nullable: true - type: number - sort: - items: - anyOf: - - type: number - - type: string - - enum: [] - nullable: true - type: array - status: - enum: - - offline - - error - - online - - inactive - - enrolling - - unenrolling - - unenrolled - - updating - - degraded - type: string - tags: - items: - type: string - type: array - type: - enum: - - PERMANENT - - EPHEMERAL - - TEMPORARY - type: string - unenrolled_at: - type: string - unenrollment_started_at: - type: string - unhealthy_reason: - items: - enum: - - input - - output - - other - type: string - nullable: true - type: array - upgrade_details: - additionalProperties: false - type: object - properties: - action_id: - type: string - metadata: - additionalProperties: false - type: object - properties: - download_percent: - type: number - download_rate: - type: number - error_msg: - type: string - failed_state: - enum: - - UPG_REQUESTED - - UPG_SCHEDULED - - UPG_DOWNLOADING - - UPG_EXTRACTING - - UPG_REPLACING - - UPG_RESTARTING - - UPG_FAILED - - UPG_WATCHING - - UPG_ROLLBACK - type: string - retry_error_msg: - type: string - retry_until: - type: string - scheduled_at: - type: string - state: - enum: - - UPG_REQUESTED - - UPG_SCHEDULED - - UPG_DOWNLOADING - - UPG_EXTRACTING - - UPG_REPLACING - - UPG_RESTARTING - - UPG_FAILED - - UPG_WATCHING - - UPG_ROLLBACK - type: string - target_version: - type: string - required: - - target_version - - action_id - - state - upgrade_started_at: - nullable: true - type: string - upgraded_at: - nullable: true - type: string - user_provided_metadata: - additionalProperties: {} - type: object - required: - - id - - packages - - type - - active - - enrolled_at - - local_metadata - type: array page: type: number perPage: @@ -19000,7 +18771,7 @@ paths: - Elastic Agents post: description: List agents by action ids - operationId: '%2Fapi%2Ffleet%2Fagents#1' + operationId: post-fleet-agents parameters: - description: The version of the API to use in: header @@ -19066,7 +18837,7 @@ paths: /api/fleet/agents/{agentId}: delete: description: Delete agent by ID - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D#2' + operationId: delete-fleet-agents-agentid parameters: - description: The version of the API to use in: header @@ -19123,7 +18894,7 @@ paths: - Elastic Agents get: description: Get agent by ID - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D#0' + operationId: get-fleet-agents-agentid parameters: - description: The version of the API to use in: header @@ -19451,7 +19222,7 @@ paths: - Elastic Agents put: description: Update agent by ID - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D#1' + operationId: put-fleet-agents-agentid parameters: - description: The version of the API to use in: header @@ -19795,7 +19566,7 @@ paths: /api/fleet/agents/{agentId}/actions: post: description: Create agent action - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Factions#0' + operationId: post-fleet-agents-agentid-actions parameters: - description: The version of the API to use in: header @@ -19940,7 +19711,7 @@ paths: /api/fleet/agents/{agentId}/reassign: post: description: Reassign agent - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Freassign#1' + operationId: post-fleet-agents-agentid-reassign parameters: - description: The version of the API to use in: header @@ -20000,47 +19771,10 @@ paths: summary: '' tags: - Elastic Agent actions - put: - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Freassign#0' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - - description: A required header to protect against CSRF attacks - in: header - name: kbn-xsrf - required: true - schema: - example: 'true' - type: string - - in: path - name: agentId - required: true - schema: - type: string - requestBody: - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - additionalProperties: false - type: object - properties: - policy_id: - type: string - required: - - policy_id - responses: {} - summary: '' - tags: [] /api/fleet/agents/{agentId}/request_diagnostics: post: description: Request agent diagnostics - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Frequest_diagnostics#0' + operationId: post-fleet-agents-agentid-request-diagnostics parameters: - description: The version of the API to use in: header @@ -20110,7 +19844,7 @@ paths: /api/fleet/agents/{agentId}/unenroll: post: description: Unenroll agent - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Funenroll#0' + operationId: post-fleet-agents-agentid-unenroll parameters: - description: The version of the API to use in: header @@ -20151,7 +19885,7 @@ paths: /api/fleet/agents/{agentId}/upgrade: post: description: Upgrade agent - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Fupgrade#0' + operationId: post-fleet-agents-agentid-upgrade parameters: - description: The version of the API to use in: header @@ -20220,7 +19954,7 @@ paths: /api/fleet/agents/{agentId}/uploads: get: description: List agent uploads - operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Fuploads#0' + operationId: get-fleet-agents-agentid-uploads parameters: - description: The version of the API to use in: header @@ -20301,7 +20035,7 @@ paths: /api/fleet/agents/action_status: get: description: Get agent action status - operationId: '%2Fapi%2Ffleet%2Fagents%2Faction_status#0' + operationId: get-fleet-agents-action-status parameters: - description: The version of the API to use in: header @@ -20469,7 +20203,7 @@ paths: /api/fleet/agents/actions/{actionId}/cancel: post: description: Cancel agent action - operationId: '%2Fapi%2Ffleet%2Fagents%2Factions%2F%7BactionId%7D%2Fcancel#0' + operationId: post-fleet-agents-actions-actionid-cancel parameters: - description: The version of the API to use in: header @@ -20564,7 +20298,7 @@ paths: /api/fleet/agents/available_versions: get: description: Get available agent versions - operationId: '%2Fapi%2Ffleet%2Fagents%2Favailable_versions#0' + operationId: get-fleet-agents-available-versions parameters: - description: The version of the API to use in: header @@ -20610,7 +20344,7 @@ paths: /api/fleet/agents/bulk_reassign: post: description: Bulk reassign agents - operationId: '%2Fapi%2Ffleet%2Fagents%2Fbulk_reassign#0' + operationId: post-fleet-agents-bulk-reassign parameters: - description: The version of the API to use in: header @@ -20684,7 +20418,7 @@ paths: /api/fleet/agents/bulk_request_diagnostics: post: description: Bulk request diagnostics from agents - operationId: '%2Fapi%2Ffleet%2Fagents%2Fbulk_request_diagnostics#0' + operationId: post-fleet-agents-bulk-request-diagnostics parameters: - description: The version of the API to use in: header @@ -20758,7 +20492,7 @@ paths: /api/fleet/agents/bulk_unenroll: post: description: Bulk unenroll agents - operationId: '%2Fapi%2Ffleet%2Fagents%2Fbulk_unenroll#0' + operationId: post-fleet-agents-bulk-unenroll parameters: - description: The version of the API to use in: header @@ -20839,7 +20573,7 @@ paths: /api/fleet/agents/bulk_update_agent_tags: post: description: Bulk update agent tags - operationId: '%2Fapi%2Ffleet%2Fagents%2Fbulk_update_agent_tags#0' + operationId: post-fleet-agents-bulk-update-agent-tags parameters: - description: The version of the API to use in: header @@ -20918,7 +20652,7 @@ paths: /api/fleet/agents/bulk_upgrade: post: description: Bulk upgrade agents - operationId: '%2Fapi%2Ffleet%2Fagents%2Fbulk_upgrade#0' + operationId: post-fleet-agents-bulk-upgrade parameters: - description: The version of the API to use in: header @@ -21003,7 +20737,7 @@ paths: /api/fleet/agents/files/{fileId}: delete: description: Delete file uploaded by agent - operationId: '%2Fapi%2Ffleet%2Fagents%2Ffiles%2F%7BfileId%7D#0' + operationId: delete-fleet-agents-files-fileid parameters: - description: The version of the API to use in: header @@ -21062,7 +20796,7 @@ paths: /api/fleet/agents/files/{fileId}/{fileName}: get: description: Get file uploaded by agent - operationId: '%2Fapi%2Ffleet%2Fagents%2Ffiles%2F%7BfileId%7D%2F%7BfileName%7D#0' + operationId: get-fleet-agents-files-fileid-filename parameters: - description: The version of the API to use in: header @@ -21110,7 +20844,7 @@ paths: /api/fleet/agents/setup: get: description: Get agent setup info - operationId: '%2Fapi%2Ffleet%2Fagents%2Fsetup#0' + operationId: get-fleet-agents-setup parameters: - description: The version of the API to use in: header @@ -21181,7 +20915,7 @@ paths: - Elastic Agents post: description: Initiate agent setup - operationId: '%2Fapi%2Ffleet%2Fagents%2Fsetup#1' + operationId: post-fleet-agents-setup parameters: - description: The version of the API to use in: header @@ -21251,7 +20985,7 @@ paths: /api/fleet/agents/tags: get: description: List agent tags - operationId: '%2Fapi%2Ffleet%2Fagents%2Ftags#0' + operationId: get-fleet-agents-tags parameters: - description: The version of the API to use in: header @@ -21308,7 +21042,7 @@ paths: /api/fleet/check-permissions: get: description: Check permissions - operationId: '%2Fapi%2Ffleet%2Fcheck-permissions#0' + operationId: get-fleet-check-permissions parameters: - description: The version of the API to use in: header @@ -21363,7 +21097,7 @@ paths: /api/fleet/data_streams: get: description: List data streams - operationId: '%2Fapi%2Ffleet%2Fdata_streams#0' + operationId: get-fleet-data-streams parameters: - description: The version of the API to use in: header @@ -21468,7 +21202,7 @@ paths: /api/fleet/enrollment_api_keys: get: description: List enrollment API keys - operationId: '%2Fapi%2Ffleet%2Fenrollment_api_keys#0' + operationId: get-fleet-enrollment-api-keys parameters: - description: The version of the API to use in: header @@ -21611,7 +21345,7 @@ paths: - Fleet enrollment API keys post: description: Create enrollment API key - operationId: '%2Fapi%2Ffleet%2Fenrollment_api_keys#1' + operationId: post-fleet-enrollment-api-keys parameters: - description: The version of the API to use in: header @@ -21715,7 +21449,7 @@ paths: /api/fleet/enrollment_api_keys/{keyId}: delete: description: Revoke enrollment API key by ID by marking it as inactive - operationId: '%2Fapi%2Ffleet%2Fenrollment_api_keys%2F%7BkeyId%7D#1' + operationId: delete-fleet-enrollment-api-keys-keyid parameters: - description: The version of the API to use in: header @@ -21772,7 +21506,7 @@ paths: - Fleet enrollment API keys get: description: Get enrollment API key by ID - operationId: '%2Fapi%2Ffleet%2Fenrollment_api_keys%2F%7BkeyId%7D#0' + operationId: get-fleet-enrollment-api-keys-keyid parameters: - description: The version of the API to use in: header @@ -21853,7 +21587,7 @@ paths: - Fleet enrollment API keys /api/fleet/enrollment-api-keys: get: - operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys#0' + operationId: get-fleet-enrollment-api-keys-2 parameters: - description: The version of the API to use in: header @@ -21884,7 +21618,7 @@ paths: summary: '' tags: [] post: - operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys#1' + operationId: post-fleet-enrollment-api-keys-2 parameters: - description: The version of the API to use in: header @@ -21921,7 +21655,7 @@ paths: tags: [] /api/fleet/enrollment-api-keys/{keyId}: delete: - operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#1' + operationId: delete-fleet-enrollment-api-keys-keyid-2 parameters: - description: The version of the API to use in: header @@ -21947,7 +21681,7 @@ paths: summary: '' tags: [] get: - operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#0' + operationId: get-fleet-enrollment-api-keys-keyid-2 parameters: - description: The version of the API to use in: header @@ -21968,7 +21702,7 @@ paths: /api/fleet/epm/bulk_assets: post: description: Bulk get assets - operationId: '%2Fapi%2Ffleet%2Fepm%2Fbulk_assets#0' + operationId: post-fleet-epm-bulk-assets parameters: - description: The version of the API to use in: header @@ -22067,7 +21801,7 @@ paths: /api/fleet/epm/categories: get: description: List package categories - operationId: '%2Fapi%2Ffleet%2Fepm%2Fcategories#0' + operationId: get-fleet-epm-categories parameters: - description: The version of the API to use in: header @@ -22165,7 +21899,7 @@ paths: /api/fleet/epm/custom_integrations: post: description: Create custom integration - operationId: '%2Fapi%2Ffleet%2Fepm%2Fcustom_integrations#0' + operationId: post-fleet-epm-custom-integrations parameters: - description: The version of the API to use in: header @@ -22361,7 +22095,7 @@ paths: /api/fleet/epm/data_streams: get: description: List data streams - operationId: '%2Fapi%2Ffleet%2Fepm%2Fdata_streams#0' + operationId: get-fleet-epm-data-streams parameters: - description: The version of the API to use in: header @@ -22444,7 +22178,7 @@ paths: /api/fleet/epm/packages: get: description: List packages - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages#0' + operationId: get-fleet-epm-packages parameters: - description: The version of the API to use in: header @@ -23198,7 +22932,7 @@ paths: - Elastic Package Manager (EPM) post: description: Install package by upload - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages#1' + operationId: post-fleet-epm-packages parameters: - description: The version of the API to use in: header @@ -23379,7 +23113,7 @@ paths: /api/fleet/epm/packages/_bulk: post: description: Bulk install packages - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F_bulk#0' + operationId: post-fleet-epm-packages-bulk parameters: - description: The version of the API to use in: header @@ -23661,7 +23395,7 @@ paths: - Elastic Package Manager (EPM) /api/fleet/epm/packages/{pkgkey}: delete: - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#3' + operationId: delete-fleet-epm-packages-pkgkey parameters: - description: The version of the API to use in: header @@ -23699,7 +23433,7 @@ paths: summary: '' tags: [] get: - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#0' + operationId: get-fleet-epm-packages-pkgkey parameters: - description: The version of the API to use in: header @@ -23739,7 +23473,7 @@ paths: summary: '' tags: [] post: - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#2' + operationId: post-fleet-epm-packages-pkgkey parameters: - description: The version of the API to use in: header @@ -23794,7 +23528,7 @@ paths: summary: '' tags: [] put: - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#1' + operationId: put-fleet-epm-packages-pkgkey parameters: - description: The version of the API to use in: header @@ -23833,7 +23567,7 @@ paths: /api/fleet/epm/packages/{pkgName}/{pkgVersion}: delete: description: Delete package - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D#3' + operationId: delete-fleet-epm-packages-pkgname-pkgversion parameters: - description: The version of the API to use in: header @@ -24013,7 +23747,7 @@ paths: - Elastic Package Manager (EPM) get: description: Get package - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D#0' + operationId: get-fleet-epm-packages-pkgname-pkgversion parameters: - description: The version of the API to use in: header @@ -24898,7 +24632,7 @@ paths: - Elastic Package Manager (EPM) post: description: Install package from registry - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D#2' + operationId: post-fleet-epm-packages-pkgname-pkgversion parameters: - description: The version of the API to use in: header @@ -25101,7 +24835,7 @@ paths: - Elastic Package Manager (EPM) put: description: Update package settings - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D#1' + operationId: put-fleet-epm-packages-pkgname-pkgversion parameters: - description: The version of the API to use in: header @@ -25976,8 +25710,7 @@ paths: /api/fleet/epm/packages/{pkgName}/{pkgVersion}/{filePath*}: get: description: Get package file - operationId: >- - %2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D%2F%7BfilePath*%7D#0 + operationId: get-fleet-epm-packages-pkgname-pkgversion-filepath parameters: - description: The version of the API to use in: header @@ -26029,8 +25762,7 @@ paths: /api/fleet/epm/packages/{pkgName}/{pkgVersion}/transforms/authorize: post: description: Authorize transforms - operationId: >- - %2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2F%7BpkgVersion%7D%2Ftransforms%2Fauthorize#0 + operationId: post-fleet-epm-packages-pkgname-pkgversion-transforms-authorize parameters: - description: The version of the API to use in: header @@ -26123,7 +25855,7 @@ paths: /api/fleet/epm/packages/{pkgName}/stats: get: description: Get package stats - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7BpkgName%7D%2Fstats#0' + operationId: get-fleet-epm-packages-pkgname-stats parameters: - description: The version of the API to use in: header @@ -26178,7 +25910,7 @@ paths: /api/fleet/epm/packages/installed: get: description: Get installed packages - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2Finstalled#0' + operationId: get-fleet-epm-packages-installed parameters: - description: The version of the API to use in: header @@ -26332,7 +26064,7 @@ paths: /api/fleet/epm/packages/limited: get: description: Get limited package list - operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2Flimited#0' + operationId: get-fleet-epm-packages-limited parameters: - description: The version of the API to use in: header @@ -26383,8 +26115,7 @@ paths: /api/fleet/epm/templates/{pkgName}/{pkgVersion}/inputs: get: description: Get inputs template - operationId: >- - %2Fapi%2Ffleet%2Fepm%2Ftemplates%2F%7BpkgName%7D%2F%7BpkgVersion%7D%2Finputs#0 + operationId: get-fleet-epm-templates-pkgname-pkgversion-inputs parameters: - description: The version of the API to use in: header @@ -26492,7 +26223,7 @@ paths: /api/fleet/epm/verification_key_id: get: description: Get a package signature verification key ID - operationId: '%2Fapi%2Ffleet%2Fepm%2Fverification_key_id#0' + operationId: get-fleet-epm-verification-key-id parameters: - description: The version of the API to use in: header @@ -26537,7 +26268,7 @@ paths: /api/fleet/fleet_server_hosts: get: description: List Fleet Server hosts - operationId: '%2Fapi%2Ffleet%2Ffleet_server_hosts#0' + operationId: get-fleet-fleet-server-hosts parameters: - description: The version of the API to use in: header @@ -26617,7 +26348,7 @@ paths: - Fleet Server hosts post: description: Create Fleet Server host - operationId: '%2Fapi%2Ffleet%2Ffleet_server_hosts#1' + operationId: post-fleet-fleet-server-hosts parameters: - description: The version of the API to use in: header @@ -26724,7 +26455,7 @@ paths: /api/fleet/fleet_server_hosts/{itemId}: delete: description: Delete Fleet Server host by ID - operationId: '%2Fapi%2Ffleet%2Ffleet_server_hosts%2F%7BitemId%7D#1' + operationId: delete-fleet-fleet-server-hosts-itemid parameters: - description: The version of the API to use in: header @@ -26779,7 +26510,7 @@ paths: - Fleet Server hosts get: description: Get Fleet Server host by ID - operationId: '%2Fapi%2Ffleet%2Ffleet_server_hosts%2F%7BitemId%7D#0' + operationId: get-fleet-fleet-server-hosts-itemid parameters: - description: The version of the API to use in: header @@ -26853,7 +26584,7 @@ paths: - Fleet Server hosts put: description: Update Fleet Server host by ID - operationId: '%2Fapi%2Ffleet%2Ffleet_server_hosts%2F%7BitemId%7D#2' + operationId: put-fleet-fleet-server-hosts-itemid parameters: - description: The version of the API to use in: header @@ -26958,7 +26689,7 @@ paths: /api/fleet/health_check: post: description: Check Fleet Server health - operationId: '%2Fapi%2Ffleet%2Fhealth_check#0' + operationId: post-fleet-health-check parameters: - description: The version of the API to use in: header @@ -27046,7 +26777,7 @@ paths: /api/fleet/kubernetes: get: description: Get full K8s agent manifest - operationId: '%2Fapi%2Ffleet%2Fkubernetes#0' + operationId: get-fleet-kubernetes parameters: - description: The version of the API to use in: header @@ -27104,7 +26835,7 @@ paths: - Elastic Agent policies /api/fleet/kubernetes/download: get: - operationId: '%2Fapi%2Ffleet%2Fkubernetes%2Fdownload#0' + operationId: get-fleet-kubernetes-download parameters: - description: The version of the API to use in: header @@ -27173,7 +26904,7 @@ paths: /api/fleet/logstash_api_keys: post: description: Generate Logstash API key - operationId: '%2Fapi%2Ffleet%2Flogstash_api_keys#0' + operationId: post-fleet-logstash-api-keys parameters: - description: The version of the API to use in: header @@ -27224,7 +26955,7 @@ paths: /api/fleet/message_signing_service/rotate_key_pair: post: description: Rotate fleet message signing key pair - operationId: '%2Fapi%2Ffleet%2Fmessage_signing_service%2Frotate_key_pair#0' + operationId: post-fleet-message-signing-service-rotate-key-pair parameters: - description: The version of the API to use in: header @@ -27297,7 +27028,7 @@ paths: /api/fleet/outputs: get: description: List outputs - operationId: '%2Fapi%2Ffleet%2Foutputs#0' + operationId: get-fleet-outputs parameters: - description: The version of the API to use in: header @@ -28053,7 +27784,7 @@ paths: - Fleet outputs post: description: Create output - operationId: '%2Fapi%2Ffleet%2Foutputs#1' + operationId: post-fleet-outputs parameters: - description: The version of the API to use in: header @@ -29513,7 +29244,7 @@ paths: /api/fleet/outputs/{outputId}: delete: description: Delete output by ID - operationId: '%2Fapi%2Ffleet%2Foutputs%2F%7BoutputId%7D#2' + operationId: delete-fleet-outputs-outputid parameters: - description: The version of the API to use in: header @@ -29584,7 +29315,7 @@ paths: - Fleet outputs get: description: Get output by ID - operationId: '%2Fapi%2Ffleet%2Foutputs%2F%7BoutputId%7D#0' + operationId: get-fleet-outputs-outputid parameters: - description: The version of the API to use in: header @@ -30334,7 +30065,7 @@ paths: - Fleet outputs put: description: Update output by ID - operationId: '%2Fapi%2Ffleet%2Foutputs%2F%7BoutputId%7D#1' + operationId: put-fleet-outputs-outputid parameters: - description: The version of the API to use in: header @@ -31778,7 +31509,7 @@ paths: /api/fleet/outputs/{outputId}/health: get: description: Get latest output health - operationId: '%2Fapi%2Ffleet%2Foutputs%2F%7BoutputId%7D%2Fhealth#0' + operationId: get-fleet-outputs-outputid-health parameters: - description: The version of the API to use in: header @@ -31836,7 +31567,7 @@ paths: /api/fleet/package_policies: get: description: List package policies - operationId: '%2Fapi%2Ffleet%2Fpackage_policies#0' + operationId: get-fleet-package-policies parameters: - description: The version of the API to use in: header @@ -32341,7 +32072,7 @@ paths: - Fleet package policies post: description: Create package policy - operationId: '%2Fapi%2Ffleet%2Fpackage_policies#1' + operationId: post-fleet-package-policies parameters: - description: The version of the API to use in: header @@ -33244,7 +32975,7 @@ paths: /api/fleet/package_policies/_bulk_get: post: description: Bulk get package policies - operationId: '%2Fapi%2Ffleet%2Fpackage_policies%2F_bulk_get#0' + operationId: post-fleet-package-policies-bulk-get parameters: - description: The version of the API to use in: header @@ -33737,7 +33468,7 @@ paths: /api/fleet/package_policies/{packagePolicyId}: delete: description: Delete package policy by ID - operationId: '%2Fapi%2Ffleet%2Fpackage_policies%2F%7BpackagePolicyId%7D#2' + operationId: delete-fleet-package-policies-packagepolicyid parameters: - description: The version of the API to use in: header @@ -33797,7 +33528,7 @@ paths: - Fleet package policies get: description: Get package policy by ID - operationId: '%2Fapi%2Ffleet%2Fpackage_policies%2F%7BpackagePolicyId%7D#0' + operationId: get-fleet-package-policies-packagepolicyid parameters: - description: The version of the API to use in: header @@ -34266,7 +33997,7 @@ paths: - Fleet package policies put: description: Update package policy by ID - operationId: '%2Fapi%2Ffleet%2Fpackage_policies%2F%7BpackagePolicyId%7D#1' + operationId: put-fleet-package-policies-packagepolicyid parameters: - description: The version of the API to use in: header @@ -35163,7 +34894,7 @@ paths: /api/fleet/package_policies/delete: post: description: Bulk delete package policies - operationId: '%2Fapi%2Ffleet%2Fpackage_policies%2Fdelete#0' + operationId: post-fleet-package-policies-delete parameters: - description: The version of the API to use in: header @@ -35300,7 +35031,7 @@ paths: /api/fleet/package_policies/upgrade: post: description: Upgrade package policy to a newer package version - operationId: '%2Fapi%2Ffleet%2Fpackage_policies%2Fupgrade#0' + operationId: post-fleet-package-policies-upgrade parameters: - description: The version of the API to use in: header @@ -35381,7 +35112,7 @@ paths: /api/fleet/package_policies/upgrade/dryrun: post: description: Dry run package policy upgrade - operationId: '%2Fapi%2Ffleet%2Fpackage_policies%2Fupgrade%2Fdryrun#0' + operationId: post-fleet-package-policies-upgrade-dryrun parameters: - description: The version of the API to use in: header @@ -36231,7 +35962,7 @@ paths: /api/fleet/proxies: get: description: List proxies - operationId: '%2Fapi%2Ffleet%2Fproxies#0' + operationId: get-fleet-proxies parameters: - description: The version of the API to use in: header @@ -36317,7 +36048,7 @@ paths: - Fleet proxies post: description: Create proxy - operationId: '%2Fapi%2Ffleet%2Fproxies#1' + operationId: post-fleet-proxies parameters: - description: The version of the API to use in: header @@ -36436,7 +36167,7 @@ paths: /api/fleet/proxies/{itemId}: delete: description: Delete proxy by ID - operationId: '%2Fapi%2Ffleet%2Fproxies%2F%7BitemId%7D#2' + operationId: delete-fleet-proxies-itemid parameters: - description: The version of the API to use in: header @@ -36491,7 +36222,7 @@ paths: - Fleet proxies get: description: Get proxy by ID - operationId: '%2Fapi%2Ffleet%2Fproxies%2F%7BitemId%7D#1' + operationId: get-fleet-proxies-itemid parameters: - description: The version of the API to use in: header @@ -36571,7 +36302,7 @@ paths: - Fleet proxies put: description: Update proxy by ID - operationId: '%2Fapi%2Ffleet%2Fproxies%2F%7BitemId%7D#0' + operationId: put-fleet-proxies-itemid parameters: - description: The version of the API to use in: header @@ -36692,7 +36423,7 @@ paths: /api/fleet/service_tokens: post: description: Create a service token - operationId: '%2Fapi%2Ffleet%2Fservice_tokens#0' + operationId: post-fleet-service-tokens parameters: - description: The version of the API to use in: header @@ -36754,33 +36485,10 @@ paths: summary: '' tags: - Fleet service tokens - /api/fleet/service-tokens: - post: - description: Create a service token - operationId: '%2Fapi%2Ffleet%2Fservice-tokens#0' - parameters: - - description: The version of the API to use - in: header - name: elastic-api-version - schema: - default: '2023-10-31' - enum: - - '2023-10-31' - type: string - - description: A required header to protect against CSRF attacks - in: header - name: kbn-xsrf - required: true - schema: - example: 'true' - type: string - responses: {} - summary: '' - tags: [] /api/fleet/settings: get: description: Get settings - operationId: '%2Fapi%2Ffleet%2Fsettings#0' + operationId: get-fleet-settings parameters: - description: The version of the API to use in: header @@ -36879,7 +36587,7 @@ paths: - Fleet internals put: description: Update settings - operationId: '%2Fapi%2Ffleet%2Fsettings#1' + operationId: put-fleet-settings parameters: - description: The version of the API to use in: header @@ -37022,7 +36730,7 @@ paths: /api/fleet/setup: post: description: Initiate Fleet setup - operationId: '%2Fapi%2Ffleet%2Fsetup#0' + operationId: post-fleet-setup parameters: - description: The version of the API to use in: header @@ -37104,7 +36812,7 @@ paths: /api/fleet/uninstall_tokens: get: description: List metadata for latest uninstall tokens per agent policy - operationId: '%2Fapi%2Ffleet%2Funinstall_tokens#0' + operationId: get-fleet-uninstall-tokens parameters: - description: The version of the API to use in: header @@ -37204,7 +36912,7 @@ paths: /api/fleet/uninstall_tokens/{uninstallTokenId}: get: description: Get one decrypted uninstall token by its ID - operationId: '%2Fapi%2Ffleet%2Funinstall_tokens%2F%7BuninstallTokenId%7D#0' + operationId: get-fleet-uninstall-tokens-uninstalltokenid parameters: - description: The version of the API to use in: header @@ -40676,7 +40384,7 @@ paths: - Prompts API /api/security/role: get: - operationId: '%2Fapi%2Fsecurity%2Frole#0' + operationId: get-security-role parameters: - description: The version of the API to use in: header @@ -40703,7 +40411,7 @@ paths: - roles /api/security/role/{name}: delete: - operationId: '%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#1' + operationId: delete-security-role-name parameters: - description: The version of the API to use in: header @@ -40733,7 +40441,7 @@ paths: tags: - roles get: - operationId: '%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#0' + operationId: get-security-role-name parameters: - description: The version of the API to use in: header @@ -40769,7 +40477,7 @@ paths: description: >- Create a new Kibana role or update the attributes of an existing role. Kibana roles are stored in the Elasticsearch native realm. - operationId: '%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#2' + operationId: put-security-role-name parameters: - description: The version of the API to use in: header @@ -41052,7 +40760,7 @@ paths: - roles /api/security/roles: post: - operationId: '%2Fapi%2Fsecurity%2Froles#0' + operationId: post-security-roles parameters: - description: The version of the API to use in: header @@ -41342,8 +41050,10 @@ paths: visualizations, data views, and saved searches, as required. You can request to overwrite any objects that already exist in the target space if they share an identifier or you can use the resolve copy saved - objects conflicts API to do this on a per-object basis. - operationId: '%2Fapi%2Fspaces%2F_copy_saved_objects#0' + objects conflicts API to do this on a per-object + basis.

[Required authorization] Route required privileges: ALL + of [copySavedObjectsToSpaces]. + operationId: post-spaces-copy-saved-objects parameters: - description: The version of the API to use in: header @@ -41430,7 +41140,7 @@ paths: - spaces /api/spaces/_disable_legacy_url_aliases: post: - operationId: '%2Fapi%2Fspaces%2F_disable_legacy_url_aliases#0' + operationId: post-spaces-disable-legacy-url-aliases parameters: - description: The version of the API to use in: header @@ -41484,7 +41194,7 @@ paths: /api/spaces/_get_shareable_references: post: description: Collect references and space contexts for saved objects. - operationId: '%2Fapi%2Fspaces%2F_get_shareable_references#0' + operationId: post-spaces-get-shareable-references parameters: - description: The version of the API to use in: header @@ -41531,8 +41241,9 @@ paths: post: description: >- Overwrite saved objects that are returned as errors from the copy saved - objects to space API. - operationId: '%2Fapi%2Fspaces%2F_resolve_copy_saved_objects_errors#0' + objects to space API.

[Required authorization] Route required + privileges: ALL of [copySavedObjectsToSpaces]. + operationId: post-spaces-resolve-copy-saved-objects-errors parameters: - description: The version of the API to use in: header @@ -41627,7 +41338,7 @@ paths: /api/spaces/_update_objects_spaces: post: description: Update one or more saved objects to add or remove them from some spaces. - operationId: '%2Fapi%2Fspaces%2F_update_objects_spaces#0' + operationId: post-spaces-update-objects-spaces parameters: - description: The version of the API to use in: header @@ -41690,7 +41401,7 @@ paths: - spaces /api/spaces/space: get: - operationId: '%2Fapi%2Fspaces%2Fspace#0' + operationId: get-spaces-space parameters: - description: The version of the API to use in: header @@ -41746,7 +41457,7 @@ paths: tags: - spaces post: - operationId: '%2Fapi%2Fspaces%2Fspace#1' + operationId: post-spaces-space parameters: - description: The version of the API to use in: header @@ -41835,7 +41546,7 @@ paths: description: >- When you delete a space, all saved objects that belong to the space are automatically deleted, which is permanent and cannot be undone. - operationId: '%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#2' + operationId: delete-spaces-space-id parameters: - description: The version of the API to use in: header @@ -41867,7 +41578,7 @@ paths: tags: - spaces get: - operationId: '%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#0' + operationId: get-spaces-space-id parameters: - description: The version of the API to use in: header @@ -41890,7 +41601,7 @@ paths: tags: - spaces put: - operationId: '%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#1' + operationId: put-spaces-space-id parameters: - description: The version of the API to use in: header @@ -41984,7 +41695,7 @@ paths: - spaces /api/status: get: - operationId: '%2Fapi%2Fstatus#0' + operationId: get-status parameters: - description: The version of the API to use in: header @@ -45976,6 +45687,24 @@ components: required: true schema: type: string + APM_UI_elastic_api_version: + description: The version of the API to use + in: header + name: elastic-api-version + required: true + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + APM_UI_kbn_xsrf: + description: A required header to protect against CSRF attacks + in: header + name: kbn-xsrf + required: true + schema: + example: 'true' + type: string Cases_alert_id: description: An identifier for the alert. in: path @@ -46467,6 +46196,471 @@ components: description: Specifies the data type for the field. example: scaled_float type: string + APM_UI_400_response: + type: object + properties: + error: + description: Error type + example: Not Found + type: string + message: + description: Error message + example: Not Found + type: string + statusCode: + description: Error status code + example: 400 + type: number + APM_UI_401_response: + type: object + properties: + error: + description: Error type + example: Unauthorized + type: string + message: + description: Error message + type: string + statusCode: + description: Error status code + example: 401 + type: number + APM_UI_403_response: + type: object + properties: + error: + description: Error type + example: Forbidden + type: string + message: + description: Error message + type: string + statusCode: + description: Error status code + example: 403 + type: number + APM_UI_404_response: + type: object + properties: + error: + description: Error type + example: Not Found + type: string + message: + description: Error message + example: Not Found + type: string + statusCode: + description: Error status code + example: 404 + type: number + APM_UI_500_response: + type: object + properties: + error: + description: Error type + example: Internal Server Error + type: string + message: + description: Error message + type: string + statusCode: + description: Error status code + example: 500 + type: number + APM_UI_501_response: + type: object + properties: + error: + description: Error type + example: Not Implemented + type: string + message: + description: Error message + example: Not Implemented + type: string + statusCode: + description: Error status code + example: 501 + type: number + APM_UI_agent_configuration_intake_object: + type: object + properties: + agent_name: + description: Agent name + type: string + service: + $ref: '#/components/schemas/APM_UI_service_object' + settings: + $ref: '#/components/schemas/APM_UI_settings_object' + required: + - service + - settings + APM_UI_agent_configuration_object: + description: Agent configuration + type: object + properties: + '@timestamp': + description: Timestamp + example: 1730194190636 + type: number + agent_name: + description: Agent name + type: string + applied_by_agent: + description: Applied by agent + example: true + type: boolean + etag: + description: Etag + example: 0bc3b5ebf18fba8163fe4c96f491e3767a358f85 + type: string + service: + $ref: '#/components/schemas/APM_UI_service_object' + settings: + $ref: '#/components/schemas/APM_UI_settings_object' + required: + - service + - settings + - '@timestamp' + - etag + APM_UI_agent_configurations_response: + type: object + properties: + configurations: + description: Agent configuration + items: + $ref: '#/components/schemas/APM_UI_agent_configuration_object' + type: array + APM_UI_agent_keys_object: + type: object + properties: + name: + description: Agent name + type: string + privileges: + description: Privileges configuration + items: + enum: + - event:write + - config_agent:read + type: string + type: array + required: + - name + - privileges + APM_UI_agent_keys_response: + type: object + properties: + agentKey: + description: Agent key + type: object + properties: + api_key: + type: string + encoded: + type: string + expiration: + format: int64 + type: integer + id: + type: string + name: + type: string + required: + - id + - name + - api_key + - encoded + APM_UI_annotation_search_response: + type: object + properties: + annotations: + description: Annotations + items: + type: object + properties: + '@timestamp': + type: number + id: + type: string + text: + type: string + type: + enum: + - version + type: string + type: array + APM_UI_base_source_map_object: + type: object + properties: + compressionAlgorithm: + description: Compression Algorithm + type: string + created: + description: Created date + type: string + decodedSha256: + description: Decoded SHA-256 + type: string + decodedSize: + description: Decoded size + type: number + encodedSha256: + description: Encoded SHA-256 + type: string + encodedSize: + description: Encoded size + type: number + encryptionAlgorithm: + description: Encryption Algorithm + type: string + id: + description: Identifier + type: string + identifier: + description: Identifier + type: string + packageName: + description: Package name + type: string + relative_url: + description: Relative URL + type: string + type: + description: Type + type: string + APM_UI_create_annotation_object: + type: object + properties: + '@timestamp': + description: Timestamp + type: string + message: + description: Message + type: string + service: + description: Service + type: object + properties: + environment: + type: string + version: + type: string + required: + - version + tags: + description: Tags + items: + type: string + type: array + required: + - '@timestamp' + - service + APM_UI_create_annotation_response: + type: object + properties: + _id: + description: Identifier + type: string + _index: + description: Index + type: string + _source: + description: Response + type: object + properties: + '@timestamp': + type: string + annotation: + type: object + properties: + title: + type: string + type: + type: string + event: + type: object + properties: + created: + type: string + message: + type: string + service: + type: object + properties: + environment: + type: string + name: + type: string + version: + type: string + tags: + items: + type: string + type: array + APM_UI_delete_agent_configurations_response: + type: object + properties: + result: + description: Result + type: string + APM_UI_search_agent_configuration_object: + type: object + properties: + etag: + description: If etags match then `applied_by_agent` field will be set to `true` + example: 0bc3b5ebf18fba8163fe4c96f491e3767a358f85 + type: string + mark_as_applied_by_agent: + description: > + `markAsAppliedByAgent=true` means "force setting it to true + regardless of etag". + + This is needed for Jaeger agent that doesn't have etags + type: boolean + service: + $ref: '#/components/schemas/APM_UI_service_object' + required: + - service + APM_UI_search_agent_configuration_response: + type: object + properties: + _id: + description: Identifier + type: string + _index: + description: Index + type: string + _score: + description: Score + type: number + _source: + $ref: '#/components/schemas/APM_UI_agent_configuration_object' + APM_UI_service_agent_name_response: + type: object + properties: + agentName: + description: Agent name + example: nodejs + type: string + APM_UI_service_environment_object: + type: object + properties: + alreadyConfigured: + description: Already configured + type: boolean + name: + description: Service environment name + example: ALL_OPTION_VALUE + type: string + APM_UI_service_environments_response: + type: object + properties: + environments: + description: Service environment list + items: + $ref: '#/components/schemas/APM_UI_service_environment_object' + type: array + APM_UI_service_object: + description: Service + type: object + properties: + environment: + description: Environment + example: prod + type: string + name: + description: Name + example: node + type: string + APM_UI_settings_object: + additionalProperties: + type: string + description: Agent configuration settings + type: object + APM_UI_single_agent_configuration_response: + allOf: + - type: object + properties: + id: + type: string + required: + - id + - $ref: '#/components/schemas/APM_UI_agent_configuration_object' + APM_UI_source_maps_response: + type: object + properties: + artifacts: + description: Artifacts + items: + allOf: + - type: object + properties: + body: + type: object + properties: + bundleFilepath: + type: string + serviceName: + type: string + serviceVersion: + type: string + sourceMap: + type: object + properties: + file: + type: string + mappings: + type: string + sourceRoot: + type: string + sources: + items: + type: string + type: array + sourcesContent: + items: + type: string + type: array + version: + type: number + - $ref: '#/components/schemas/APM_UI_base_source_map_object' + type: array + APM_UI_upload_source_map_object: + type: object + properties: + bundle_filepath: + description: >- + The absolute path of the final bundle as used in the web + application. + type: string + service_name: + description: The name of the service that the service map should apply to. + type: string + service_version: + description: The version of the service that the service map should apply to. + type: string + sourcemap: + description: > + The source map. String or file upload. It must follow the + + [source map revision 3 + proposal](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k). + format: binary + type: string + required: + - service_name + - service_version + - bundle_filepath + - sourcemap + APM_UI_upload_source_maps_response: + allOf: + - type: object + properties: + body: + type: string + - $ref: '#/components/schemas/APM_UI_base_source_map_object' Cases_4xx_response: properties: error: @@ -57138,6 +57332,8 @@ components: Security_Entity_Analytics_API_EngineDescriptor: type: object properties: + error: + type: object fieldHistoryLength: type: integer filter: @@ -60523,6 +60719,9 @@ security: - basicAuth: [] tags: - name: alerting + - description: | + Adjust APM agent configuration without need to redeploy your application. + name: APM agent configuration - description: > Configure APM agent keys to authorize requests from APM agents to the APM Server. @@ -60532,6 +60731,10 @@ tags: Annotations enable you to easily see how events are impacting the performance of your applications. name: APM annotations + - description: Create APM fleet server schema. + name: APM server schema + - description: Configure APM source maps. + name: APM sourcemaps - description: Case APIs enable you to open and track issues. name: cases - name: connectors diff --git a/oas_docs/scripts/merge_ess_oas.js b/oas_docs/scripts/merge_ess_oas.js index 218e8dfa5b803..d8bc45e64c2f2 100644 --- a/oas_docs/scripts/merge_ess_oas.js +++ b/oas_docs/scripts/merge_ess_oas.js @@ -22,7 +22,7 @@ const { REPO_ROOT } = require('@kbn/repo-info'); `${REPO_ROOT}/packages/core/saved-objects/docs/openapi/bundled.yaml`, // Observability Solution - `${REPO_ROOT}/x-pack/plugins/observability_solution/apm/docs/openapi/apm.yaml`, + `${REPO_ROOT}/x-pack/plugins/observability_solution/apm/docs/openapi/apm/bundled.yaml`, `${REPO_ROOT}/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml`, // Security solution diff --git a/oas_docs/scripts/merge_serverless_oas.js b/oas_docs/scripts/merge_serverless_oas.js index c66187dea8d8d..63d2df0f32d3f 100644 --- a/oas_docs/scripts/merge_serverless_oas.js +++ b/oas_docs/scripts/merge_serverless_oas.js @@ -20,7 +20,7 @@ const { REPO_ROOT } = require('@kbn/repo-info'); `${REPO_ROOT}/packages/core/saved-objects/docs/openapi/bundled_serverless.yaml`, // Observability Solution - `${REPO_ROOT}/x-pack/plugins/observability_solution/apm/docs/openapi/apm.yaml`, + `${REPO_ROOT}/x-pack/plugins/observability_solution/apm/docs/openapi/apm/bundled.yaml`, `${REPO_ROOT}/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml`, // Security solution diff --git a/package.json b/package.json index 271a5eea7e9ba..574c6afec9fed 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "resolutions": { "**/@bazel/typescript/protobufjs": "6.11.4", "**/@hello-pangea/dnd": "16.6.0", - "**/@langchain/core": "^0.2.18", + "**/@langchain/core": "^0.3.16", "**/@langchain/google-common": "^0.1.1", "**/@types/node": "20.10.5", "**/@typescript-eslint/utils": "5.62.0", @@ -90,7 +90,7 @@ "**/globule/minimatch": "^3.1.2", "**/hoist-non-react-statics": "^3.3.2", "**/isomorphic-fetch/node-fetch": "^2.6.7", - "**/langchain": "^0.2.11", + "**/langchain": "^0.3.5", "**/remark-parse/trim": "1.0.1", "**/sharp": "0.32.6", "**/typescript": "5.1.6", @@ -118,7 +118,7 @@ "@elastic/ecs": "^8.11.1", "@elastic/elasticsearch": "^8.15.0", "@elastic/ems-client": "8.5.3", - "@elastic/eui": "97.2.0", + "@elastic/eui": "97.3.0", "@elastic/filesaver": "1.1.2", "@elastic/node-crypto": "^1.2.3", "@elastic/numeral": "^2.5.1", @@ -571,6 +571,7 @@ "@kbn/index-management-plugin": "link:x-pack/plugins/index_management", "@kbn/index-management-shared-types": "link:x-pack/packages/index-management/index_management_shared_types", "@kbn/index-patterns-test-plugin": "link:test/plugin_functional/plugins/index_patterns", + "@kbn/inference-common": "link:x-pack/packages/ai-infra/inference-common", "@kbn/inference-plugin": "link:x-pack/plugins/inference", "@kbn/inference_integration_flyout": "link:x-pack/packages/ml/inference_integration_flyout", "@kbn/infra-forge": "link:x-pack/packages/kbn-infra-forge", @@ -809,7 +810,6 @@ "@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-role-management-model": "link:x-pack/packages/security/role_management_model", - "@kbn/security-solution-common": "link:x-pack/packages/security-solution/common", "@kbn/security-solution-distribution-bar": "link:x-pack/packages/security-solution/distribution_bar", "@kbn/security-solution-ess": "link:x-pack/plugins/security_solution_ess", "@kbn/security-solution-features": "link:x-pack/packages/security-solution/features", @@ -1009,15 +1009,15 @@ "@kbn/xstate-utils": "link:packages/kbn-xstate-utils", "@kbn/zod": "link:packages/kbn-zod", "@kbn/zod-helpers": "link:packages/kbn-zod-helpers", - "@langchain/community": "0.2.18", - "@langchain/core": "^0.2.18", + "@langchain/community": "0.3.11", + "@langchain/core": "^0.3.16", "@langchain/google-common": "^0.1.1", - "@langchain/google-genai": "^0.1.0", + "@langchain/google-genai": "^0.1.2", "@langchain/google-vertexai": "^0.1.0", - "@langchain/langgraph": "0.0.34", - "@langchain/openai": "^0.1.3", + "@langchain/langgraph": "0.2.19", + "@langchain/openai": "^0.3.11", "@langtrase/trace-attributes": "^3.0.8", - "@launchdarkly/node-server-sdk": "^9.6.1", + "@launchdarkly/node-server-sdk": "^9.7.0", "@launchdarkly/openfeature-node-server": "^1.0.0", "@loaders.gl/core": "^3.4.7", "@loaders.gl/json": "^3.4.7", @@ -1111,7 +1111,7 @@ "del": "^6.1.0", "diff": "^5.1.0", "dotenv": "^16.4.5", - "elastic-apm-node": "^4.8.0", + "elastic-apm-node": "^4.8.1", "email-addresses": "^5.0.0", "eventsource-parser": "^1.1.1", "execa": "^5.1.1", @@ -1158,9 +1158,9 @@ "jsonwebtoken": "^9.0.2", "jsts": "^1.6.2", "kea": "^2.6.0", - "langchain": "^0.2.11", - "langsmith": "^0.1.55", - "launchdarkly-js-client-sdk": "^3.4.0", + "langchain": "^0.3.5", + "langsmith": "^0.2.3", + "launchdarkly-js-client-sdk": "^3.5.0", "load-json-file": "^6.2.0", "lodash": "^4.17.21", "lru-cache": "^4.1.5", @@ -1188,7 +1188,7 @@ "nunjucks": "^3.2.4", "object-hash": "^1.3.1", "object-path-immutable": "^3.1.1", - "openai": "^4.24.1", + "openai": "^4.68.0", "openpgp": "5.10.1", "ora": "^4.0.4", "p-limit": "^3.0.1", @@ -1497,7 +1497,7 @@ "@octokit/rest": "^17.11.2", "@parcel/watcher": "^2.1.0", "@playwright/test": "=1.46.0", - "@redocly/cli": "^1.25.7", + "@redocly/cli": "^1.25.9", "@statoscope/webpack-plugin": "^5.28.2", "@storybook/addon-a11y": "^6.5.16", "@storybook/addon-actions": "^6.5.16", @@ -1579,7 +1579,7 @@ "@types/jsonwebtoken": "^9.0.0", "@types/license-checker": "15.0.0", "@types/loader-utils": "^2.0.3", - "@types/lodash": "^4.17.12", + "@types/lodash": "^4.17.13", "@types/lru-cache": "^5.1.0", "@types/lz-string": "^1.3.34", "@types/mapbox__vector-tile": "1.3.0", @@ -1675,7 +1675,7 @@ "buildkite-test-collector": "^1.7.0", "callsites": "^3.1.0", "chance": "1.0.18", - "chromedriver": "^129.0.0", + "chromedriver": "^130.0.1", "clarify": "^2.2.0", "clean-webpack-plugin": "^3.0.0", "cli-progress": "^3.12.0", @@ -1718,13 +1718,13 @@ "exit-hook": "^2.2.0", "expect": "^29.7.0", "expose-loader": "^0.7.5", - "express": "^4.21.0", + "express": "^4.21.1", "faker": "^5.1.0", "fetch-mock": "^7.3.9", "file-loader": "^4.2.0", "find-cypress-specs": "^1.41.4", "form-data": "^4.0.0", - "geckodriver": "^4.4.4", + "geckodriver": "^4.5.1", "gulp-brotli": "^3.0.0", "gulp-postcss": "^9.0.1", "gulp-terser": "^2.1.0", diff --git a/packages/content-management/table_list_view_table/src/table_list_view.test.tsx b/packages/content-management/table_list_view_table/src/table_list_view.test.tsx index 38e05299184e4..38229399f2ec8 100644 --- a/packages/content-management/table_list_view_table/src/table_list_view.test.tsx +++ b/packages/content-management/table_list_view_table/src/table_list_view.test.tsx @@ -242,8 +242,8 @@ describe('TableListView', () => { const updatedAtValues: Moment[] = []; const updatedHits = hits.map(({ id, attributes, references }, i) => { - const updatedAt = new Date(new Date().setDate(new Date().getDate() - (7 + i))); - updatedAtValues.push(moment(updatedAt)); + const updatedAt = moment().subtract(7 + i, 'days'); + updatedAtValues.push(updatedAt); return { id, diff --git a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx index 5d86209ec8800..434639b07efdf 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx @@ -36,6 +36,7 @@ import type { ChromeSetProjectBreadcrumbsParams, NavigationTreeDefinition, AppDeepLinkId, + SolutionId, } from '@kbn/core-chrome-browser'; import type { CustomBrandingStart } from '@kbn/core-custom-branding-browser'; import type { @@ -343,7 +344,10 @@ export class ChromeService { LinkId extends AppDeepLinkId = AppDeepLinkId, Id extends string = string, ChildrenId extends string = Id - >(id: string, navigationTree$: Observable>) { + >( + id: SolutionId, + navigationTree$: Observable> + ) { validateChromeStyle(); projectNavigation.initNavigation(id, navigationTree$); } 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 d1be94aad246a..124b44e80e30f 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 @@ -110,7 +110,7 @@ describe('initNavigation()', () => { beforeAll(() => { projectNavigation.initNavigation( - 'foo', + 'es', of({ body: [ { @@ -185,7 +185,7 @@ describe('initNavigation()', () => { const { projectNavigation: projNavigation, getNavigationTree: getNavTree } = setupInitNavigation(); projNavigation.initNavigation( - 'foo', + 'es', of({ body: [ { @@ -210,7 +210,7 @@ describe('initNavigation()', () => { const { projectNavigation: projNavigation } = setupInitNavigation(); projNavigation.initNavigation( - 'foo', + 'es', of({ body: [ { @@ -399,7 +399,7 @@ describe('initNavigation()', () => { // 2. initNavigation() is called projectNavigation.initNavigation( - 'foo', + 'es', of({ body: [ { @@ -427,7 +427,7 @@ describe('initNavigation()', () => { }); projectNavigation.initNavigation( - 'foo', + 'es', // @ts-expect-error - We pass a non valid cloudLink that is not TS valid of({ body: [ @@ -533,7 +533,7 @@ describe('breadcrumbs', () => { const obs = subj.asObservable(); if (initiateNavigation) { - projectNavigation.initNavigation('foo', obs); + projectNavigation.initNavigation('es', obs); } return { @@ -740,7 +740,7 @@ describe('breadcrumbs', () => { { text: 'custom1', href: '/custom1' }, { text: 'custom2', href: '/custom1/custom2' }, ]); - projectNavigation.initNavigation('foo', of(mockNavigation)); // init navigation + projectNavigation.initNavigation('es', of(mockNavigation)); // init navigation const breadcrumbs = await firstValueFrom(projectNavigation.getProjectBreadcrumbs$()); expect(breadcrumbs).toHaveLength(4); @@ -779,7 +779,7 @@ describe('getActiveNodes$()', () => { expect(activeNodes).toEqual([]); projectNavigation.initNavigation( - 'foo', + 'es', of({ body: [ { @@ -835,7 +835,7 @@ describe('getActiveNodes$()', () => { expect(activeNodes).toEqual([]); projectNavigation.initNavigation( - 'foo', + 'es', of({ body: [ { @@ -889,7 +889,7 @@ describe('getActiveNodes$()', () => { describe('solution navigations', () => { const solution1: SolutionNavigationDefinition = { - id: 'solution1', + id: 'es', title: 'Solution 1', icon: 'logoSolution1', homePage: 'discover', @@ -897,7 +897,7 @@ describe('solution navigations', () => { }; const solution2: SolutionNavigationDefinition = { - id: 'solution2', + id: 'oblt', title: 'Solution 2', icon: 'logoSolution2', homePage: 'app2', @@ -906,7 +906,7 @@ describe('solution navigations', () => { }; const solution3: SolutionNavigationDefinition = { - id: 'solution3', + id: 'security', title: 'Solution 3', icon: 'logoSolution3', homePage: 'discover', @@ -943,30 +943,30 @@ describe('solution navigations', () => { } { - projectNavigation.updateSolutionNavigations({ 1: solution1, 2: solution2 }); + projectNavigation.updateSolutionNavigations({ es: solution1, oblt: solution2 }); const solutionNavs = await lastValueFrom( projectNavigation.getSolutionsNavDefinitions$().pipe(take(1)) ); - expect(solutionNavs).toEqual({ 1: solution1, 2: solution2 }); + expect(solutionNavs).toEqual({ es: solution1, oblt: solution2 }); } { // Test partial update - projectNavigation.updateSolutionNavigations({ 3: solution3 }, false); + projectNavigation.updateSolutionNavigations({ security: solution3 }, false); const solutionNavs = await lastValueFrom( projectNavigation.getSolutionsNavDefinitions$().pipe(take(1)) ); - expect(solutionNavs).toEqual({ 1: solution1, 2: solution2, 3: solution3 }); + expect(solutionNavs).toEqual({ es: solution1, oblt: solution2, security: solution3 }); } { // Test full replacement - projectNavigation.updateSolutionNavigations({ 4: solution3 }, true); + projectNavigation.updateSolutionNavigations({ security: solution3 }, true); const solutionNavs = await lastValueFrom( projectNavigation.getSolutionsNavDefinitions$().pipe(take(1)) ); - expect(solutionNavs).toEqual({ 4: solution3 }); + expect(solutionNavs).toEqual({ security: solution3 }); } }); @@ -980,8 +980,8 @@ describe('solution navigations', () => { expect(activeSolution).toBeNull(); } - projectNavigation.changeActiveSolutionNavigation('2'); // Set **before** the navs are registered - projectNavigation.updateSolutionNavigations({ 1: solution1, 2: solution2 }); + projectNavigation.changeActiveSolutionNavigation('oblt'); // Set **before** the navs are registered + projectNavigation.updateSolutionNavigations({ es: solution1, oblt: solution2 }); { const activeSolution = await lastValueFrom( @@ -994,7 +994,7 @@ describe('solution navigations', () => { expect(activeSolution).toEqual(rest); } - projectNavigation.changeActiveSolutionNavigation('1'); // Set **after** the navs are registered + projectNavigation.changeActiveSolutionNavigation('es'); // Set **after** the navs are registered { const activeSolution = await lastValueFrom( @@ -1027,7 +1027,7 @@ describe('solution navigations', () => { { const fooSolution: SolutionNavigationDefinition = { - id: 'fooSolution', + id: 'es', title: 'Foo solution', icon: 'logoSolution', homePage: 'discover', @@ -1053,8 +1053,8 @@ describe('solution navigations', () => { }), }; - projectNavigation.changeActiveSolutionNavigation('foo'); - projectNavigation.updateSolutionNavigations({ foo: fooSolution }); + projectNavigation.changeActiveSolutionNavigation('es'); + projectNavigation.updateSolutionNavigations({ es: fooSolution }); projectNavigation.setPanelSelectedNode('link2'); // Set the selected node using its id diff --git a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts index 85c3fd1905adb..7960d9f710c90 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts @@ -17,6 +17,7 @@ import type { NavigationTreeDefinition, SolutionNavigationDefinitions, CloudLinks, + SolutionId, } from '@kbn/core-chrome-browser'; import type { InternalHttpStart } from '@kbn/core-http-browser-internal'; import { @@ -86,9 +87,9 @@ export class ProjectNavigationService { private readonly solutionNavDefinitions$ = new BehaviorSubject({}); // As the active definition **id** and the definitions are set independently, one before the other without // any guarantee of order, we need to store the next active definition id in a separate BehaviorSubject - private readonly nextSolutionNavDefinitionId$ = new BehaviorSubject(null); + private readonly nextSolutionNavDefinitionId$ = new BehaviorSubject(null); // The active solution navigation definition id that has been initiated and is currently active - private readonly activeSolutionNavDefinitionId$ = new BehaviorSubject(null); + private readonly activeSolutionNavDefinitionId$ = new BehaviorSubject(null); private readonly location$ = new BehaviorSubject(createLocation('/')); private deepLinksMap$: Observable> = of({}); private cloudLinks$ = new BehaviorSubject({}); @@ -138,7 +139,7 @@ export class ProjectNavigationService { return this.projectName$.asObservable(); }, initNavigation: ( - id: string, + id: SolutionId, navTreeDefinition$: Observable> ) => { this.initNavigation(id, navTreeDefinition$); @@ -202,7 +203,7 @@ export class ProjectNavigationService { * @param id Id for the navigation tree definition * @param navTreeDefinition$ The navigation tree definition */ - private initNavigation(id: string, navTreeDefinition$: Observable) { + private initNavigation(id: SolutionId, navTreeDefinition$: Observable) { if (this.activeSolutionNavDefinitionId$.getValue() === id) return; if (this.navigationChangeSubscription) { @@ -220,7 +221,7 @@ export class ProjectNavigationService { .pipe( takeUntil(this.stop$), map(([def, deepLinksMap, cloudLinks]) => { - return parseNavigationTree(def, { + return parseNavigationTree(id, def, { deepLinks: deepLinksMap, cloudLinks, }); @@ -382,7 +383,7 @@ export class ProjectNavigationService { this.projectHome$.next(homeHref); } - private changeActiveSolutionNavigation(id: string | null) { + private changeActiveSolutionNavigation(id: SolutionId | null) { if (this.nextSolutionNavDefinitionId$.getValue() === id) return; this.nextSolutionNavDefinitionId$.next(id); } @@ -400,7 +401,7 @@ export class ProjectNavigationService { if (!definitions[id]) return null; // We strip out the sideNavComponent from the definition as it should only be used internally - const { sideNavComponent, ...definition } = definitions[id]; + const { sideNavComponent, ...definition } = definitions[id]!; return definition; }) ); 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 9a45290c95389..bdf3929c464dc 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 @@ -22,6 +22,7 @@ import type { CloudLinkId, CloudLinks, ItemDefinition, + SolutionId, } from '@kbn/core-chrome-browser/src'; import type { Location } from 'history'; import type { MouseEventHandler } from 'react'; @@ -364,6 +365,7 @@ const isRecentlyAccessedDefinition = ( }; export const parseNavigationTree = ( + id: SolutionId, navigationTreeDef: NavigationTreeDefinition, { deepLinks, cloudLinks }: { deepLinks: Record; cloudLinks: CloudLinks } ): { @@ -376,7 +378,7 @@ export const parseNavigationTree = ( const navigationTree: ChromeProjectNavigationNode[] = []; // Contains UI layout information (body, footer) and render "special" blocks like recently accessed. - const navigationTreeUI: NavigationTreeDefinitionUI = { body: [] }; + const navigationTreeUI: NavigationTreeDefinitionUI = { id, body: [] }; const initNodeAndChildren = ( node: GroupDefinition | ItemDefinition | NodeDefinition, diff --git a/packages/core/chrome/core-chrome-browser-internal/src/types.ts b/packages/core/chrome/core-chrome-browser-internal/src/types.ts index 0e6bec4d2678c..36a247e22f847 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/types.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/types.ts @@ -18,6 +18,7 @@ import type { NavigationTreeDefinitionUI, CloudURLs, SolutionNavigationDefinitions, + SolutionId, } from '@kbn/core-chrome-browser'; import type { Observable } from 'rxjs'; @@ -66,7 +67,7 @@ export interface InternalChromeStart extends ChromeStart { Id extends string = string, ChildrenId extends string = Id >( - id: string, + id: SolutionId, navigationTree$: Observable> ): void; @@ -117,6 +118,6 @@ export interface InternalChromeStart extends ChromeStart { * @param id The id of the active solution navigation. If `null` is provided, the solution navigation * will be replaced with the legacy Kibana navigation. */ - changeActiveSolutionNavigation(id: string | null): void; + changeActiveSolutionNavigation(id: SolutionId | null): void; }; } diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/loading_indicator.scss b/packages/core/chrome/core-chrome-browser-internal/src/ui/loading_indicator.scss index ccf1ecc873fc5..d12331d9c042d 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/loading_indicator.scss +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/loading_indicator.scss @@ -2,3 +2,8 @@ visibility: hidden; animation-play-state: paused; } + +.euiHeaderSectionItem .euiButtonEmpty__text { + // stop global header buttons from jumping during loading state + display: flex; +} \ No newline at end of file diff --git a/packages/core/chrome/core-chrome-browser/index.ts b/packages/core/chrome/core-chrome-browser/index.ts index afb2050d12e80..7b8658791340f 100644 --- a/packages/core/chrome/core-chrome-browser/index.ts +++ b/packages/core/chrome/core-chrome-browser/index.ts @@ -60,4 +60,5 @@ export type { SolutionNavigationDefinitions, EuiSideNavItemTypeEnhanced, RenderAs, + SolutionId, } from './src'; diff --git a/packages/core/chrome/core-chrome-browser/src/index.ts b/packages/core/chrome/core-chrome-browser/src/index.ts index efc2fb5636d84..efc709ff512da 100644 --- a/packages/core/chrome/core-chrome-browser/src/index.ts +++ b/packages/core/chrome/core-chrome-browser/src/index.ts @@ -38,6 +38,7 @@ export type { PanelSelectedNode, AppDeepLinkId, AppId, + SolutionId, CloudLinkId, CloudLink, CloudLinks, 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 3e6afeb8f6117..f4a5af26c4176 100644 --- a/packages/core/chrome/core-chrome-browser/src/project_navigation.ts +++ b/packages/core/chrome/core-chrome-browser/src/project_navigation.ts @@ -42,6 +42,8 @@ import type { AppId as SharedApp, DeepLinkId as SharedLink } from '@kbn/deeplink import type { ChromeNavLink } from './nav_links'; import type { ChromeRecentlyAccessedHistoryItem } from './recently_accessed'; +export type SolutionId = 'es' | 'oblt' | 'security'; + /** @public */ export type AppId = | DevToolsApp @@ -414,6 +416,7 @@ export interface NavigationTreeDefinition< * with their corresponding "deepLink"...) */ export interface NavigationTreeDefinitionUI { + id: SolutionId; body: Array; footer?: Array; } @@ -429,7 +432,7 @@ export interface NavigationTreeDefinitionUI { export interface SolutionNavigationDefinition { /** Unique id for the solution navigation. */ - id: string; + id: SolutionId; /** Title for the solution navigation. */ title: string; /** The navigation tree definition */ @@ -442,9 +445,9 @@ export interface SolutionNavigationDefinition { 'euiDisplaySelector.labelExpanded': i18n.translate('core.euiDisplaySelector.labelExpanded', { defaultMessage: 'Expanded', }), - 'euiDisplaySelector.labelSingle': i18n.translate('core.euiDisplaySelector.labelSingle', { - defaultMessage: 'Single', - }), 'euiDisplaySelector.labelAuto': i18n.translate('core.euiDisplaySelector.labelAuto', { - defaultMessage: 'Auto fit', + defaultMessage: 'Auto', }), - 'euiDisplaySelector.labelCustom': i18n.translate('core.euiDisplaySelector.labelCustom', { - defaultMessage: 'Custom', + 'euiDisplaySelector.labelStatic': i18n.translate('core.euiDisplaySelector.labelStatic', { + defaultMessage: 'Static', }), - 'euiDisplaySelector.rowHeightLabel': i18n.translate('core.euiDisplaySelector.rowHeightLabel', { - defaultMessage: 'Row height', + 'euiDisplaySelector.labelMax': i18n.translate('core.euiDisplaySelector.labelMax', { + defaultMessage: 'Max', }), - 'euiDisplaySelector.lineCountLabel': i18n.translate('core.euiDisplaySelector.lineCountLabel', { + 'euiDisplaySelector.rowHeightLabel': i18n.translate('core.euiDisplaySelector.rowHeightLabel', { defaultMessage: 'Lines per row', }), 'euiFieldPassword.showPassword': i18n.translate('core.euiFieldPassword.showPassword', { diff --git a/packages/core/root/core-root-browser-internal/src/kbn_bootstrap.ts b/packages/core/root/core-root-browser-internal/src/kbn_bootstrap.ts index 80020b79427f5..a06abd107fd06 100644 --- a/packages/core/root/core-root-browser-internal/src/kbn_bootstrap.ts +++ b/packages/core/root/core-root-browser-internal/src/kbn_bootstrap.ts @@ -39,6 +39,76 @@ export async function __kbnBootstrap__() { }), ]); + const isDomStorageDisabled = () => { + try { + const key = 'kbn_bootstrap_domStorageEnabled'; + sessionStorage.setItem(key, 'true'); + sessionStorage.removeItem(key); + localStorage.setItem(key, 'true'); + localStorage.removeItem(key); + return false; + } catch (e) { + return true; + } + }; + + if (isDomStorageDisabled()) { + const defaultErrorTitle = `Couldn't load the page`; + const defaultErrorText = `Update your browser's settings to allow storage of cookies and site data, and reload the page.`; + const defaultErrorReload = 'Reload'; + + const errorTitle = i18nError + ? defaultErrorTitle + : i18n.translate('core.ui.welcomeErrorCouldNotLoadPage', { + defaultMessage: defaultErrorTitle, + }); + + const errorText = i18nError + ? defaultErrorText + : i18n.translate('core.ui.welcomeErrorDomStorageDisabled', { + defaultMessage: defaultErrorText, + }); + + const errorReload = i18nError + ? defaultErrorReload + : i18n.translate('core.ui.welcomeErrorReloadButton', { + defaultMessage: defaultErrorReload, + }); + + const err = document.createElement('div'); + err.style.textAlign = 'center'; + err.style.padding = '120px 20px'; + err.style.fontFamily = 'Inter, BlinkMacSystemFont, Helvetica, Arial, sans-serif'; + + const errorTitleEl = document.createElement('h1'); + errorTitleEl.innerText = errorTitle; + errorTitleEl.style.margin = '20px'; + errorTitleEl.style.color = '#1a1c21'; + + const errorTextEl = document.createElement('p'); + errorTextEl.innerText = errorText; + errorTextEl.style.margin = '20px'; + errorTextEl.style.color = '#343741'; + + const errorReloadEl = document.createElement('button'); + errorReloadEl.innerText = errorReload; + errorReloadEl.onclick = function () { + location.reload(); + }; + errorReloadEl.setAttribute( + 'style', + 'cursor: pointer; padding-inline: 12px; block-size: 40px; font-size: 1rem; line-height: 1.4286rem; border-radius: 6px; min-inline-size: 112px; color: rgb(255, 255, 255); background-color: rgb(0, 119, 204); outline-color: rgb(0, 0, 0); border:none' + ); + + err.appendChild(errorTitleEl); + err.appendChild(errorTextEl); + err.appendChild(errorReloadEl); + + document.body.innerHTML = ''; + document.body.appendChild(err); + return; + } + const coreSystem = new CoreSystem({ injectedMetadata, rootDomElement: document.body, diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/helpers/common.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/helpers/common.ts index 27bd0918b0f9b..870f6833b4edc 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/helpers/common.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/helpers/common.ts @@ -105,10 +105,14 @@ export class CommonHelper { if (!id) { return SavedObjectsUtils.generateId(); } - // only allow a specified ID if we're overwriting an existing ESO with a Version - // this helps us ensure that the document really was previously created using ESO - // and not being used to get around the specified ID limitation - const canSpecifyID = (overwrite && version) || SavedObjectsUtils.isRandomId(id); + + const shouldEnforceRandomId = this.encryptionExtension?.shouldEnforceRandomId(type); + + // Allow specified ID if: + // 1. we're overwriting an existing ESO with a Version (this helps us ensure that the document really was previously created using ESO) + // 2. enforceRandomId is explicitly set to false + const canSpecifyID = + !shouldEnforceRandomId || (overwrite && version) || SavedObjectsUtils.isRandomId(id); if (!canSpecifyID) { throw SavedObjectsErrorHelpers.createBadRequestError( 'Predefined IDs are not allowed for saved objects with encrypted attributes unless the ID is a UUID.' diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.encryption_extension.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.encryption_extension.test.ts index f5c8c8518a58a..cf66621565577 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.encryption_extension.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.encryption_extension.test.ts @@ -261,6 +261,7 @@ describe('SavedObjectsRepository Encryption Extension', () => { it(`fails if non-UUID ID is specified for encrypted type`, async () => { mockEncryptionExt.isEncryptableType.mockReturnValue(true); + mockEncryptionExt.shouldEnforceRandomId.mockReturnValue(true); mockEncryptionExt.decryptOrStripResponseAttributes.mockResolvedValue({ ...encryptedSO, ...decryptedStrippedAttributes, @@ -291,6 +292,25 @@ describe('SavedObjectsRepository Encryption Extension', () => { ).resolves.not.toThrowError(); }); + it('allows to opt-out of random ID enforcement', async () => { + mockEncryptionExt.isEncryptableType.mockReturnValue(true); + mockEncryptionExt.shouldEnforceRandomId.mockReturnValue(false); + mockEncryptionExt.decryptOrStripResponseAttributes.mockResolvedValue({ + ...encryptedSO, + ...decryptedStrippedAttributes, + }); + + const result = await repository.create(encryptedSO.type, encryptedSO.attributes, { + id: encryptedSO.id, + version: mockVersion, + }); + + expect(client.create).toHaveBeenCalled(); + expect(mockEncryptionExt.isEncryptableType).toHaveBeenCalledWith(encryptedSO.type); + expect(mockEncryptionExt.shouldEnforceRandomId).toHaveBeenCalledWith(encryptedSO.type); + expect(result.id).toBe(encryptedSO.id); + }); + describe('namespace', () => { const doTest = async (optNamespace: string, expectNamespaceInDescriptor: boolean) => { const options = { overwrite: true, namespace: optNamespace }; @@ -483,6 +503,7 @@ describe('SavedObjectsRepository Encryption Extension', () => { it(`fails if non-UUID ID is specified for encrypted type`, async () => { mockEncryptionExt.isEncryptableType.mockReturnValue(true); + mockEncryptionExt.shouldEnforceRandomId.mockReturnValue(true); const result = await bulkCreateSuccess(client, repository, [ encryptedSO, // Predefined IDs are not allowed for saved objects with encrypted attributes unless the ID is a UUID ]); @@ -529,6 +550,25 @@ describe('SavedObjectsRepository Encryption Extension', () => { expect(result.saved_objects.length).toBe(1); expect(result.saved_objects[0].error).toBeUndefined(); }); + + it('allows to opt-out of random ID enforcement', async () => { + mockEncryptionExt.isEncryptableType.mockReturnValue(true); + mockEncryptionExt.shouldEnforceRandomId.mockReturnValue(false); + mockEncryptionExt.decryptOrStripResponseAttributes.mockResolvedValue({ + ...encryptedSO, + ...decryptedStrippedAttributes, + }); + + const result = await bulkCreateSuccess(client, repository, [ + { ...encryptedSO, version: mockVersion }, + ]); + + expect(client.bulk).toHaveBeenCalled(); + expect(result.saved_objects).not.toBeUndefined(); + expect(result.saved_objects.length).toBe(1); + expect(result.saved_objects[0].error).toBeUndefined(); + expect(result.saved_objects[0].id).toBe(encryptedSO.id); + }); }); describe('#bulkUpdate', () => { diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/mocks/saved_objects_extensions.mock.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/mocks/saved_objects_extensions.mock.ts index 2061bb63240b2..9dc7c0f0133c5 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/mocks/saved_objects_extensions.mock.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/mocks/saved_objects_extensions.mock.ts @@ -17,6 +17,7 @@ const createEncryptionExtension = (): jest.Mocked => ({ diff --git a/packages/core/saved-objects/core-saved-objects-api-server-mocks/src/saved_objects_extensions.mock.ts b/packages/core/saved-objects/core-saved-objects-api-server-mocks/src/saved_objects_extensions.mock.ts index 2a2d121b568be..776ecfe3a7385 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-mocks/src/saved_objects_extensions.mock.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-mocks/src/saved_objects_extensions.mock.ts @@ -18,6 +18,7 @@ const createEncryptionExtension = (): jest.Mocked => ({ diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/import_saved_objects.test.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/import_saved_objects.test.ts index d8c2b0b25874f..06c8e351bc445 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/import_saved_objects.test.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/import_saved_objects.test.ts @@ -19,6 +19,7 @@ import { } from './import_saved_objects.test.mock'; import { Readable } from 'stream'; +import { loggerMock, type MockedLogger } from '@kbn/logging-mocks'; import { v4 as uuidv4 } from 'uuid'; import type { SavedObjectsImportFailure, @@ -40,8 +41,10 @@ import { import type { ImportStateMap } from './lib'; describe('#importSavedObjectsFromStream', () => { + let logger: MockedLogger; beforeEach(() => { jest.clearAllMocks(); + logger = loggerMock.create(); // mock empty output of each of these mocked modules so the import doesn't throw an error mockCollectSavedObjects.mockResolvedValue({ errors: [], @@ -72,7 +75,6 @@ describe('#importSavedObjectsFromStream', () => { let savedObjectsClient: jest.Mocked; let typeRegistry: jest.Mocked; const namespace = 'some-namespace'; - const setupOptions = ({ createNewCopies = false, getTypeImpl = (type: string) => @@ -102,6 +104,7 @@ describe('#importSavedObjectsFromStream', () => { createNewCopies, importHooks, managed, + log: logger, }; }; const createObject = ({ diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/import_saved_objects.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/import_saved_objects.ts index 4182daa610b37..7d6bf9668286a 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/import_saved_objects.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/import_saved_objects.ts @@ -17,6 +17,7 @@ import type { ISavedObjectTypeRegistry, SavedObjectsImportHook, } from '@kbn/core-saved-objects-server'; +import type { Logger } from '@kbn/logging'; import { checkReferenceOrigins, validateReferences, @@ -59,6 +60,7 @@ export interface ImportSavedObjectsOptions { * If provided, Kibana will apply the given option to the `managed` property. */ managed?: boolean; + log: Logger; } /** @@ -79,7 +81,11 @@ export async function importSavedObjectsFromStream({ refresh, compatibilityMode, managed, + log, }: ImportSavedObjectsOptions): Promise { + log.debug( + `Importing with overwrite ${overwrite ? 'enabled' : 'disabled'} and size limit ${objectLimit}` + ); let errorAccumulator: SavedObjectsImportFailure[] = []; const supportedTypes = typeRegistry.getImportableAndExportableTypes().map((type) => type.name); @@ -90,6 +96,11 @@ export async function importSavedObjectsFromStream({ supportedTypes, managed, }); + log.debug( + `Importing types: ${[ + ...new Set(collectSavedObjectsResult.collectedObjects.map((obj) => obj.type)), + ].join(', ')}` + ); errorAccumulator = [...errorAccumulator, ...collectSavedObjectsResult.errors]; // Map of all IDs for objects that we are attempting to import, and any references that are not included in the read stream; // each value is empty by default @@ -197,7 +208,17 @@ export async function importSavedObjectsFromStream({ objects: createSavedObjectsResult.createdObjects, importHooks, }); - + if (errorAccumulator.length > 0) { + log.error( + `Failed to import saved objects. ${errorAccumulator.length} errors: ${JSON.stringify( + errorAccumulator + )}` + ); + } else { + log.info( + `Successfully imported ${createSavedObjectsResult.createdObjects.length} saved objects.` + ); + } return { successCount: createSavedObjectsResult.createdObjects.length, success: errorAccumulator.length === 0, diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/saved_objects_importer.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/saved_objects_importer.ts index f990eb13c435b..e8c13180bbdaf 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/saved_objects_importer.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/saved_objects_importer.ts @@ -62,7 +62,6 @@ export class SavedObjectsImporter implements ISavedObjectsImporter { compatibilityMode, managed, }: SavedObjectsImportOptions): Promise { - this.#log.debug('Starting the import process'); return importSavedObjectsFromStream({ readStream, createNewCopies, @@ -75,6 +74,7 @@ export class SavedObjectsImporter implements ISavedObjectsImporter { typeRegistry: this.#typeRegistry, importHooks: this.#importHooks, managed, + log: this.#log, }); } diff --git a/packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts b/packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts index 3fdb29203fe13..4560ab5672666 100644 --- a/packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts +++ b/packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts @@ -39,6 +39,14 @@ export interface ISavedObjectsEncryptionExtension { */ isEncryptableType: (type: string) => boolean; + /** + * Returns false if ESO type explicitly opts out of highly random UID + * + * @param type the string name of the object type + * @returns boolean, true by default unless explicitly set to false + */ + shouldEnforceRandomId: (type: string) => boolean; + /** * Given a saved object, will return a decrypted saved object or will strip * attributes from the returned object if decryption fails. diff --git a/packages/deeplinks/search/constants.ts b/packages/deeplinks/search/constants.ts index a2a17b20efba8..9848bb0c3d42e 100644 --- a/packages/deeplinks/search/constants.ts +++ b/packages/deeplinks/search/constants.ts @@ -21,3 +21,7 @@ export const SERVERLESS_ES_SEARCH_INFERENCE_ENDPOINTS_ID = 'searchInferenceEndpo export const SEARCH_HOMEPAGE = 'searchHomepage'; export const SEARCH_INDICES_START = 'elasticsearchStart'; export const SEARCH_INDICES = 'elasticsearchIndices'; +export const SEARCH_ELASTICSEARCH = 'enterpriseSearchElasticsearch'; +export const SEARCH_VECTOR_SEARCH = 'enterpriseSearchVectorSearch'; +export const SEARCH_SEMANTIC_SEARCH = 'enterpriseSearchSemanticSearch'; +export const SEARCH_AI_SEARCH = 'enterpriseSearchAISearch'; diff --git a/packages/deeplinks/search/deep_links.ts b/packages/deeplinks/search/deep_links.ts index 98703f18ac3fb..22dfb91bdff33 100644 --- a/packages/deeplinks/search/deep_links.ts +++ b/packages/deeplinks/search/deep_links.ts @@ -22,6 +22,10 @@ import { SEARCH_HOMEPAGE, SEARCH_INDICES_START, SEARCH_INDICES, + SEARCH_ELASTICSEARCH, + SEARCH_VECTOR_SEARCH, + SEARCH_SEMANTIC_SEARCH, + SEARCH_AI_SEARCH, } from './constants'; export type EnterpriseSearchApp = typeof ENTERPRISE_SEARCH_APP_ID; @@ -38,6 +42,10 @@ export type SearchInferenceEndpointsId = typeof SERVERLESS_ES_SEARCH_INFERENCE_E export type SearchHomepage = typeof SEARCH_HOMEPAGE; export type SearchStart = typeof SEARCH_INDICES_START; export type SearchIndices = typeof SEARCH_INDICES; +export type SearchElasticsearch = typeof SEARCH_ELASTICSEARCH; +export type SearchVectorSearch = typeof SEARCH_VECTOR_SEARCH; +export type SearchSemanticSearch = typeof SEARCH_SEMANTIC_SEARCH; +export type SearchAISearch = typeof SEARCH_AI_SEARCH; export type ContentLinkId = 'searchIndices' | 'connectors' | 'webCrawlers'; @@ -65,4 +73,8 @@ export type DeepLinkId = | `${EnterpriseSearchAppsearchApp}:${AppsearchLinkId}` | `${EnterpriseSearchRelevanceApp}:${RelevanceLinkId}` | SearchStart - | SearchIndices; + | SearchIndices + | SearchElasticsearch + | SearchVectorSearch + | SearchSemanticSearch + | SearchAISearch; diff --git a/packages/deeplinks/search/index.ts b/packages/deeplinks/search/index.ts index 250dfeed299e6..7c78d64081133 100644 --- a/packages/deeplinks/search/index.ts +++ b/packages/deeplinks/search/index.ts @@ -17,6 +17,10 @@ export { ENTERPRISE_SEARCH_WORKPLACESEARCH_APP_ID, SERVERLESS_ES_APP_ID, SERVERLESS_ES_CONNECTORS_ID, + SEARCH_ELASTICSEARCH, + SEARCH_VECTOR_SEARCH, + SEARCH_SEMANTIC_SEARCH, + SEARCH_AI_SEARCH, } from './constants'; export type { diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_uptime_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_uptime_schema.ts index 8770b70402d08..bf37ffc1ddb9c 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_uptime_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_uptime_schema.ts @@ -77,6 +77,7 @@ const ObservabilityUptimeAlertOptional = rt.partial({ 'anomaly.start': schemaDate, configId: schemaString, 'error.message': schemaString, + 'error.stack_trace': schemaString, 'host.name': schemaString, 'kibana.alert.context': schemaUnknown, 'kibana.alert.evaluation.threshold': schemaStringOrNumber, diff --git a/packages/kbn-alerts-ui-shared/src/rule_type_modal/components/rule_type_list.test.tsx b/packages/kbn-alerts-ui-shared/src/rule_type_modal/components/rule_type_list.test.tsx index 175d90924586c..7003b06875659 100644 --- a/packages/kbn-alerts-ui-shared/src/rule_type_modal/components/rule_type_list.test.tsx +++ b/packages/kbn-alerts-ui-shared/src/rule_type_modal/components/rule_type_list.test.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { render, screen, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { RuleTypeList } from './rule_type_list'; import { RuleTypeWithDescription } from '../types'; @@ -93,5 +94,8 @@ describe('RuleTypeList', () => { expect(secondRuleInList).not.toBeDisabled(); const thirdRuleInList = within(ruleListEl[2]).getByRole('button', { name: 'Rule Type 2' }); expect(thirdRuleInList).toBeDisabled(); + + await userEvent.hover(ruleListEl[2]); + expect(await screen.findByText('This rule requires a platinum license.')).toBeInTheDocument(); }); }); diff --git a/packages/kbn-alerts-ui-shared/src/rule_type_modal/components/rule_type_list.tsx b/packages/kbn-alerts-ui-shared/src/rule_type_modal/components/rule_type_list.tsx index 4e21e428f3c5c..91710dbdfade5 100644 --- a/packages/kbn-alerts-ui-shared/src/rule_type_modal/components/rule_type_list.tsx +++ b/packages/kbn-alerts-ui-shared/src/rule_type_modal/components/rule_type_list.tsx @@ -20,6 +20,7 @@ import { EuiEmptyPrompt, EuiButton, useEuiTheme, + EuiToolTip, } from '@elastic/eui'; import { omit } from 'lodash'; import { PRODUCER_DISPLAY_NAMES } from '../../common/i18n'; @@ -86,6 +87,28 @@ export const RuleTypeList: React.FC = ({ const onClickAll = useCallback(() => onFilterByProducer(null), [onFilterByProducer]); + const ruleCard = (rule: RuleTypeWithDescription) => ( + onSelectRuleType(rule.id)} + description={rule.description} + style={{ marginRight: '8px', flexGrow: 0 }} + data-test-subj={`${rule.id}-SelectOption`} + isDisabled={!rule.enabledInLicense} + > + + {producerToDisplayName(rule.producer)} + + + ); + return ( = ({ )} {ruleTypesList.map((rule) => ( - onSelectRuleType(rule.id)} - description={rule.description} - style={{ marginRight: '8px', flexGrow: 0 }} - data-test-subj={`${rule.id}-SelectOption`} - isDisabled={rule.enabledInLicense === false} - > - - {producerToDisplayName(rule.producer)} - - + <>{ruleCard(rule)} + + )} ))} diff --git a/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/index.ts b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/index.ts index 36d7f8caf9601..db95dcf4155bc 100644 --- a/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/index.ts +++ b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/index.ts @@ -55,9 +55,9 @@ export class K8sEntity extends Serializable { super({ ...fields, 'entity.type': entityTypeWithSchema, - 'entity.definitionId': `builtin_${entityTypeWithSchema}`, - 'entity.identityFields': identityFields, - 'entity.displayName': getDisplayName({ identityFields, fields }), + 'entity.definition_id': `builtin_${entityTypeWithSchema}`, + 'entity.identity_fields': identityFields, + 'entity.display_name': getDisplayName({ identityFields, fields }), }); } } diff --git a/packages/kbn-apm-synthtrace-client/src/types/agent_names.ts b/packages/kbn-apm-synthtrace-client/src/types/agent_names.ts index d181a437fb73d..5953331bf5aa8 100644 --- a/packages/kbn-apm-synthtrace-client/src/types/agent_names.ts +++ b/packages/kbn-apm-synthtrace-client/src/types/agent_names.ts @@ -24,6 +24,8 @@ type OpenTelemetryAgentName = | 'otlp' | 'opentelemetry/cpp' | 'opentelemetry/dotnet' + | 'opentelemetry/dotnet/opentelemetry-dotnet-instrumentation' + | 'opentelemetry/dotnet/elastic' | 'opentelemetry/erlang' | 'opentelemetry/go' | 'opentelemetry/java' diff --git a/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts b/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts index 307f43932e94b..e9e5add3db075 100644 --- a/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts +++ b/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts @@ -16,10 +16,12 @@ import { getFetchAgent } from '../../../cli/utils/ssl'; export class ApmSynthtraceKibanaClient { private readonly logger: Logger; private target: string; + private headers: Record; - constructor(options: { logger: Logger; target: string }) { + constructor(options: { logger: Logger; target: string; headers?: Record }) { this.logger = options.logger; this.target = options.target; + this.headers = { ...kibanaHeaders(), ...(options.headers ?? {}) }; } getFleetApmPackagePath(packageVersion?: string): string { @@ -39,7 +41,7 @@ export class ApmSynthtraceKibanaClient { const response = await fetch(url, { method: 'GET', - headers: kibanaHeaders(), + headers: this.headers, agent: getFetchAgent(url), }); @@ -79,7 +81,7 @@ export class ApmSynthtraceKibanaClient { async () => { const res = await fetch(url, { method: 'POST', - headers: kibanaHeaders(), + headers: this.headers, body: '{"force":true}', agent: getFetchAgent(url), }); @@ -127,7 +129,7 @@ export class ApmSynthtraceKibanaClient { async () => { const res = await fetch(url, { method: 'DELETE', - headers: kibanaHeaders(), + headers: this.headers, body: '{"force":true}', agent: getFetchAgent(url), }); diff --git a/packages/kbn-apm-synthtrace/src/scenarios/helpers/unstructured_logs.ts b/packages/kbn-apm-synthtrace/src/scenarios/helpers/unstructured_logs.ts index 490bd449e2b60..9f2e2f76dfee6 100644 --- a/packages/kbn-apm-synthtrace/src/scenarios/helpers/unstructured_logs.ts +++ b/packages/kbn-apm-synthtrace/src/scenarios/helpers/unstructured_logs.ts @@ -33,7 +33,7 @@ export const unstructuredLogMessageGenerators = { ])} successfully ${f.number.int({ max: 100000 })} times`, ], taskStatusSuccess: (f: Faker) => [ - `${f.hacker.noun()}: ${f.word.words()} ${f.helpers.arrayElement([ + `${f.hacker.noun()}: ${f.word.words()} ${f.string.uuid()} ${f.helpers.arrayElement([ 'triggered', 'executed', 'processed', @@ -46,7 +46,7 @@ export const unstructuredLogMessageGenerators = { 'execution', 'processing', 'handling', - ])} of ${f.word.words()} failed at ${f.date.recent().toISOString()}`, + ])} of ${f.string.uuid()} failed at ${f.date.recent().toISOString()}`, ], error: (f: Faker) => [ `${f.helpers.arrayElement([ @@ -58,7 +58,7 @@ export const unstructuredLogMessageGenerators = { 'Issue', ])}: ${f.hacker.phrase()}`, `Stopping ${f.number.int(42)} background tasks...`, - 'Shutting down process...', + `Shutting down process ${f.string.hexadecimal({ length: 16, prefix: '' })}...`, ], restart: (f: Faker) => { const service = f.database.engine(); @@ -72,13 +72,27 @@ export const unstructuredLogMessageGenerators = { ])}`, ]; }, - userAuthentication: (f: Faker) => [ - `User ${f.internet.userName()} ${f.helpers.arrayElement([ - 'logged in', - 'logged out', - 'failed to login', - ])}`, - ], + userAuthentication: (f: Faker) => + f.helpers.arrayElements( + [ + `User ${f.internet.userName()} (id ${f.string.uuid()}) ${f.helpers.arrayElement([ + 'logged in', + 'logged out', + ])} at ${f.date.recent().toISOString()} from ${f.internet.ip()}:${f.internet.port()}`, + `Created new user ${f.internet.userName()} (id ${f.string.uuid()})`, + `Disabled user ${f.internet.userName()} (id ${f.string.uuid()}) due to level ${f.number.int( + { max: 10 } + )} ${f.helpers.arrayElement([ + 'suspicious activity', + 'security concerns', + 'policy violation', + ])}`, + `Login ${f.internet.userName()} (id ${f.string.uuid()}) incorrect ${f.number.int({ + max: 100, + })} times from ${f.internet.ipv6()}.`, + ], + { min: 1, max: 3 } + ), networkEvent: (f: Faker) => [ `Network ${f.helpers.arrayElement([ 'connection', diff --git a/packages/kbn-cell-actions/src/actions/copy_to_clipboard/copy_to_clipboard.ts b/packages/kbn-cell-actions/src/actions/copy_to_clipboard/copy_to_clipboard.ts index 850a534278fbe..90e93923fa360 100644 --- a/packages/kbn-cell-actions/src/actions/copy_to_clipboard/copy_to_clipboard.ts +++ b/packages/kbn-cell-actions/src/actions/copy_to_clipboard/copy_to_clipboard.ts @@ -33,7 +33,7 @@ const COPY_TO_CLIPBOARD_SUCCESS = i18n.translate( } ); -const escapeValue = (value: string) => value.replace(/"/g, '\\"'); +const escapeValue = (value: string) => value.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); export const createCopyToClipboardActionFactory = createCellActionFactory( ({ notifications }: { notifications: NotificationsStart }) => ({ diff --git a/packages/kbn-data-stream-adapter/src/field_maps/types.ts b/packages/kbn-data-stream-adapter/src/field_maps/types.ts index 62f4c7c600036..1cdafc7c61809 100644 --- a/packages/kbn-data-stream-adapter/src/field_maps/types.ts +++ b/packages/kbn-data-stream-adapter/src/field_maps/types.ts @@ -54,6 +54,8 @@ export type FieldMap = Record< scaling_factor?: number; dynamic?: boolean | 'strict'; properties?: Record; + inference_id?: string; + copy_to?: string; } >; diff --git a/packages/kbn-discover-utils/index.ts b/packages/kbn-discover-utils/index.ts index 7234944783037..4345c0f8fc6c4 100644 --- a/packages/kbn-discover-utils/index.ts +++ b/packages/kbn-discover-utils/index.ts @@ -56,6 +56,7 @@ export { getVisibleColumns, canPrependTimeFieldColumn, DiscoverFlyouts, + AppMenuRegistry, dismissAllFlyoutsExceptFor, dismissFlyouts, LogLevelBadge, diff --git a/packages/kbn-discover-utils/src/components/app_menu/__snapshots__/app_menu_registry.test.ts.snap b/packages/kbn-discover-utils/src/components/app_menu/__snapshots__/app_menu_registry.test.ts.snap new file mode 100644 index 0000000000000..88ee3c6f55a76 --- /dev/null +++ b/packages/kbn-discover-utils/src/components/app_menu/__snapshots__/app_menu_registry.test.ts.snap @@ -0,0 +1,184 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AppMenuRegistry should allow to override actions under submenu 1`] = ` +Array [ + Object { + "controlProps": Object { + "label": "Action 2", + "onClick": [MockFunction], + }, + "id": "action-2", + "order": 200, + "type": "secondary", + }, + Object { + "actions": Array [ + Object { + "controlProps": Object { + "label": "Action 3.2", + "onClick": [MockFunction], + }, + "id": "action-3-2", + "order": 200, + "type": "secondary", + }, + Object { + "controlProps": Object { + "label": "Action Custom", + "onClick": [MockFunction], + }, + "id": "action-3-1", + "type": "custom", + }, + ], + "id": "action-3", + "label": "Action 3", + "order": 300, + "type": "secondary", + }, + Object { + "controlProps": Object { + "iconType": "bell", + "label": "Action 1", + "onClick": [MockFunction], + }, + "id": "action-1", + "order": 100, + "type": "primary", + }, +] +`; + +exports[`AppMenuRegistry should allow to register custom actions 1`] = ` +Array [ + Object { + "controlProps": Object { + "label": "Action Custom", + "onClick": [MockFunction], + }, + "id": "action-custom", + "type": "custom", + }, + Object { + "actions": Array [ + Object { + "controlProps": Object { + "label": "Action Custom Submenu 1", + "onClick": [MockFunction], + }, + "id": "action-custom-submenu-1", + "type": "custom", + }, + ], + "id": "action-custom-submenu", + "label": "Action Custom Submenu", + "type": "custom", + }, + Object { + "controlProps": Object { + "label": "Action 2", + "onClick": [MockFunction], + }, + "id": "action-2", + "order": 200, + "type": "secondary", + }, + Object { + "actions": Array [ + Object { + "controlProps": Object { + "iconType": "heart", + "label": "Action 3.1", + "onClick": [MockFunction], + }, + "id": "action-3-1", + "order": 100, + "type": "secondary", + }, + Object { + "controlProps": Object { + "label": "Action 3.2", + "onClick": [MockFunction], + }, + "id": "action-3-2", + "order": 200, + "type": "secondary", + }, + ], + "id": "action-3", + "label": "Action 3", + "order": 300, + "type": "secondary", + }, + Object { + "controlProps": Object { + "iconType": "bell", + "label": "Action 1", + "onClick": [MockFunction], + }, + "id": "action-1", + "order": 100, + "type": "primary", + }, +] +`; + +exports[`AppMenuRegistry should allow to register custom actions under submenu 1`] = ` +Array [ + Object { + "controlProps": Object { + "label": "Action 2", + "onClick": [MockFunction], + }, + "id": "action-2", + "order": 200, + "type": "secondary", + }, + Object { + "actions": Array [ + Object { + "controlProps": Object { + "iconType": "heart", + "label": "Action 3.1", + "onClick": [MockFunction], + }, + "id": "action-3-1", + "order": 100, + "type": "secondary", + }, + Object { + "controlProps": Object { + "label": "Action Custom", + "onClick": [MockFunction], + }, + "id": "action-custom", + "order": 101, + "type": "custom", + }, + Object { + "controlProps": Object { + "label": "Action 3.2", + "onClick": [MockFunction], + }, + "id": "action-3-2", + "order": 200, + "type": "secondary", + }, + ], + "id": "action-3", + "label": "Action 3", + "order": 300, + "type": "secondary", + }, + Object { + "controlProps": Object { + "iconType": "bell", + "label": "Action 1", + "onClick": [MockFunction], + }, + "id": "action-1", + "order": 100, + "type": "primary", + }, +] +`; diff --git a/packages/kbn-discover-utils/src/components/app_menu/app_menu_registry.test.ts b/packages/kbn-discover-utils/src/components/app_menu/app_menu_registry.test.ts new file mode 100644 index 0000000000000..46b565c490b0e --- /dev/null +++ b/packages/kbn-discover-utils/src/components/app_menu/app_menu_registry.test.ts @@ -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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { AppMenuRegistry } from './app_menu_registry'; +import { + AppMenuActionSubmenuSecondary, + AppMenuActionType, + AppMenuSubmenuActionCustom, +} from './types'; + +describe('AppMenuRegistry', () => { + it('should initialize correctly', () => { + const appMenuRegistry = initializeAppMenuRegistry(); + expect(appMenuRegistry.isActionRegistered('action-1')).toBe(true); + expect(appMenuRegistry.isActionRegistered('action-2')).toBe(true); + expect(appMenuRegistry.isActionRegistered('action-3')).toBe(true); + expect(appMenuRegistry.isActionRegistered('action-3-1')).toBe(true); + expect(appMenuRegistry.isActionRegistered('action-3-2')).toBe(true); + expect(appMenuRegistry.isActionRegistered('action-n')).toBe(false); + expect(appMenuRegistry.getSortedItems()).toHaveLength(3); + }); + + it('should allow to register custom actions', () => { + const appMenuRegistry = initializeAppMenuRegistry(); + expect(appMenuRegistry.isActionRegistered('action-custom')).toBe(false); + + appMenuRegistry.registerCustomAction({ + id: 'action-custom', + type: AppMenuActionType.custom, + controlProps: { + label: 'Action Custom', + onClick: jest.fn(), + }, + }); + + appMenuRegistry.registerCustomAction({ + id: 'action-custom-submenu', + type: AppMenuActionType.custom, + label: 'Action Custom Submenu', + actions: [ + { + id: 'action-custom-submenu-1', + type: AppMenuActionType.custom, + controlProps: { + label: 'Action Custom Submenu 1', + onClick: jest.fn(), + }, + }, + ], + }); + + expect(appMenuRegistry.isActionRegistered('action-custom')).toBe(true); + expect(appMenuRegistry.isActionRegistered('action-custom-submenu')).toBe(true); + expect(appMenuRegistry.getSortedItems()).toHaveLength(5); + + appMenuRegistry.registerCustomAction({ + id: 'action-custom-extra', + type: AppMenuActionType.custom, + controlProps: { + label: 'Action Custom Extra', + onClick: jest.fn(), + }, + }); + + // should limit the number of custom items + const items = appMenuRegistry.getSortedItems(); + expect(items).toHaveLength(5); + expect(items).toMatchSnapshot(); + }); + + it('should allow to register custom actions under submenu', () => { + const appMenuRegistry = initializeAppMenuRegistry(); + expect(appMenuRegistry.isActionRegistered('action-custom')).toBe(false); + + let items = appMenuRegistry.getSortedItems(); + let submenuItem = items.find((item) => item.id === 'action-3') as AppMenuActionSubmenuSecondary; + expect(items).toHaveLength(3); + expect(submenuItem.actions).toHaveLength(2); + + appMenuRegistry.registerCustomActionUnderSubmenu('action-3', { + id: 'action-custom', + type: AppMenuActionType.custom, + order: 101, + controlProps: { + label: 'Action Custom', + onClick: jest.fn(), + }, + }); + + expect(appMenuRegistry.isActionRegistered('action-custom')).toBe(true); + + items = appMenuRegistry.getSortedItems(); + expect(items).toHaveLength(3); + + // calling it again should not add a duplicate + items = appMenuRegistry.getSortedItems(); + expect(items).toHaveLength(3); + + submenuItem = items.find((item) => item.id === 'action-3') as AppMenuActionSubmenuSecondary; + expect(submenuItem.actions).toHaveLength(3); + expect(items).toMatchSnapshot(); + }); + + it('should allow to override actions under submenu', () => { + const appMenuRegistry = initializeAppMenuRegistry(); + + let items = appMenuRegistry.getSortedItems(); + expect(items).toHaveLength(3); + + let submenuItem = items.find((item) => item.id === 'action-3') as AppMenuActionSubmenuSecondary; + const existingSecondaryActionId = submenuItem.actions[0].id; + expect(submenuItem.actions).toHaveLength(2); + + expect(appMenuRegistry.isActionRegistered(existingSecondaryActionId)).toBe(true); + + const customAction: AppMenuSubmenuActionCustom = { + id: existingSecondaryActionId, // using the same id to override the action with a custom one + type: AppMenuActionType.custom, + controlProps: { + label: 'Action Custom', + onClick: jest.fn(), + }, + }; + appMenuRegistry.registerCustomActionUnderSubmenu('action-3', customAction); + + expect(appMenuRegistry.isActionRegistered(existingSecondaryActionId)).toBe(true); + + items = appMenuRegistry.getSortedItems(); + submenuItem = items.find((item) => item.id === 'action-3') as AppMenuActionSubmenuSecondary; + expect(submenuItem.actions).toHaveLength(2); + expect(submenuItem.actions.find((item) => item.id === existingSecondaryActionId)).toBe( + customAction + ); + expect(items).toMatchSnapshot(); + }); +}); + +function initializeAppMenuRegistry() { + return new AppMenuRegistry([ + { + id: 'action-1', + type: AppMenuActionType.primary, + controlProps: { + label: 'Action 1', + iconType: 'bell', + onClick: jest.fn(), + }, + }, + { + id: 'action-2', + type: AppMenuActionType.secondary, + controlProps: { + label: 'Action 2', + onClick: jest.fn(), + }, + }, + { + id: 'action-3', + type: AppMenuActionType.secondary, + label: 'Action 3', + actions: [ + { + id: 'action-3-1', + type: AppMenuActionType.secondary, + controlProps: { + label: 'Action 3.1', + iconType: 'heart', + onClick: jest.fn(), + }, + }, + { + id: 'action-3-2', + type: AppMenuActionType.secondary, + controlProps: { + label: 'Action 3.2', + onClick: jest.fn(), + }, + }, + ], + }, + ]); +} diff --git a/packages/kbn-discover-utils/src/components/app_menu/app_menu_registry.ts b/packages/kbn-discover-utils/src/components/app_menu/app_menu_registry.ts new file mode 100644 index 0000000000000..65145c7de6751 --- /dev/null +++ b/packages/kbn-discover-utils/src/components/app_menu/app_menu_registry.ts @@ -0,0 +1,214 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { + AppMenuActionBase, + AppMenuActionSubmenuBase, + AppMenuActionSubmenuCustom, + AppMenuSubmenuHorizontalRule, + AppMenuActionSubmenuSecondary, + AppMenuActionType, + AppMenuItem, + AppMenuItemCustom, + AppMenuItemPrimary, + AppMenuItemSecondary, + AppMenuSubmenuActionCustom, +} from './types'; + +export class AppMenuRegistry { + static CUSTOM_ITEMS_LIMIT = 2; + + private appMenuItems: AppMenuItem[]; + /** + * As custom actions can be registered under a submenu from both root and data source profiles, we need to keep track of them separately. + * Otherwise, it would be less predictable. For example, we would override/reset the actions from the data source profile with the ones from the root profile. + * @private + */ + private customSubmenuItemsBySubmenuId: Map< + string, + Array + >; + + constructor(primaryAndSecondaryActions: Array) { + this.appMenuItems = assignOrderToActions(primaryAndSecondaryActions); + this.customSubmenuItemsBySubmenuId = new Map(); + } + + public isActionRegistered(appMenuItemId: string) { + return ( + this.appMenuItems.some((item) => { + if (item.id === appMenuItemId) { + return true; + } + if (isAppMenuActionSubmenu(item)) { + return item.actions.some((submenuItem) => submenuItem.id === appMenuItemId); + } + return false; + }) || + [...this.customSubmenuItemsBySubmenuId.values()].some((submenuItems) => + submenuItems.some((item) => item.id === appMenuItemId) + ) + ); + } + + /** + * Register a custom action to the app menu. It can be a simple action or a submenu with more actions and horizontal rules. + * Note: Only 2 top level custom actions are allowed to be rendered in the app menu. The rest will be ignored. + * A custom action can also open a flyout or a modal. For that, return your custom react node from action's `onClick` event and call `onFinishAction` when you're done. + * @param appMenuItem + */ + public registerCustomAction(appMenuItem: AppMenuItemCustom) { + this.appMenuItems = [ + ...this.appMenuItems.filter( + // prevent duplicates + (item) => !(item.id === appMenuItem.id && item.type === AppMenuActionType.custom) + ), + appMenuItem, + ]; + } + + /** + * Register a custom action under a submenu. It can be an action or a horizontal rule. + * Any number of submenu actions can be registered and rendered. + * You can also extend an existing submenu with more actions. For example, AppMenuActionType.alerts. + * `order` property is optional and can be used to control the order of actions in the submenu. + * @param submenuId + * @param appMenuItem + */ + public registerCustomActionUnderSubmenu( + submenuId: string, + appMenuItem: AppMenuSubmenuActionCustom | AppMenuSubmenuHorizontalRule + ) { + this.customSubmenuItemsBySubmenuId.set(submenuId, [ + ...(this.customSubmenuItemsBySubmenuId.get(submenuId) ?? []).filter( + // prevent duplicates and allow overrides + (item) => item.id !== appMenuItem.id + ), + appMenuItem, + ]); + } + + private getSortedItemsForType(type: AppMenuActionType) { + let actions = this.appMenuItems.filter((item) => item.type === type); + + if (type === AppMenuActionType.custom && actions.length > AppMenuRegistry.CUSTOM_ITEMS_LIMIT) { + // apply the limitation on how many custom items can be shown + actions = actions.slice(0, AppMenuRegistry.CUSTOM_ITEMS_LIMIT); + } + + // enrich submenus with custom actions + if (type === AppMenuActionType.secondary || type === AppMenuActionType.custom) { + [...this.customSubmenuItemsBySubmenuId.entries()].forEach(([submenuId, customActions]) => { + actions = actions.map((item) => { + if (item.id === submenuId && isAppMenuActionSubmenu(item)) { + return extendSubmenuWithCustomActions(item, customActions); + } + return item; + }); + }); + } + + return sortAppMenuItemsByOrder(actions); + } + + /** + * Get the resulting app menu items sorted by type and order. + */ + public getSortedItems() { + const primaryItems = this.getSortedItemsForType(AppMenuActionType.primary); + const secondaryItems = this.getSortedItemsForType(AppMenuActionType.secondary); + const customItems = this.getSortedItemsForType(AppMenuActionType.custom); + + return [...customItems, ...secondaryItems, ...primaryItems]; + } +} + +function isAppMenuActionSubmenu( + appMenuItem: AppMenuItem +): appMenuItem is AppMenuActionSubmenuSecondary | AppMenuActionSubmenuCustom { + return 'actions' in appMenuItem && Array.isArray(appMenuItem.actions); +} + +const FALLBACK_ORDER = Number.MAX_SAFE_INTEGER; + +function sortByOrder(a: T, b: T): number { + return (a.order ?? FALLBACK_ORDER) - (b.order ?? FALLBACK_ORDER); +} + +function getAppMenuSubmenuWithSortedItemsByOrder< + T extends AppMenuActionSubmenuBase = AppMenuActionSubmenuSecondary | AppMenuActionSubmenuCustom +>(appMenuItem: T): T { + return { + ...appMenuItem, + actions: [...appMenuItem.actions].sort(sortByOrder), + }; +} + +function sortAppMenuItemsByOrder(appMenuItems: AppMenuItem[]): AppMenuItem[] { + const sortedAppMenuItems = [...appMenuItems].sort(sortByOrder); + return sortedAppMenuItems.map((appMenuItem) => { + if (isAppMenuActionSubmenu(appMenuItem)) { + return getAppMenuSubmenuWithSortedItemsByOrder(appMenuItem); + } + return appMenuItem; + }); +} + +function getAppMenuSubmenuWithAssignedOrder< + T extends AppMenuActionSubmenuBase = AppMenuActionSubmenuSecondary | AppMenuActionSubmenuCustom +>(appMenuItem: T, order: number): T { + let orderInSubmenu = 0; + const actionsWithOrder = appMenuItem.actions.map((action) => { + orderInSubmenu = orderInSubmenu + 100; + return { + ...action, + order: action.order ?? orderInSubmenu, + }; + }); + return { + ...appMenuItem, + order: appMenuItem.order ?? order, + actions: actionsWithOrder, + }; +} + +function extendSubmenuWithCustomActions< + T extends AppMenuActionSubmenuBase = AppMenuActionSubmenuSecondary | AppMenuActionSubmenuCustom +>( + appMenuItem: T, + customActions: Array +): T { + const customActionsIds = new Set(customActions.map((action) => action.id)); + return { + ...appMenuItem, + actions: [ + ...appMenuItem.actions.filter((item) => !customActionsIds.has(item.id)), // allow to override secondary actions with custom ones + ...customActions, + ], + }; +} + +/** + * All primary and secondary actions by default get order 100, 200, 300,... assigned to them. + * Same for actions under a submenu. + * @param appMenuItems + */ +function assignOrderToActions(appMenuItems: AppMenuItem[]): AppMenuItem[] { + let order = 0; + return appMenuItems.map((appMenuItem) => { + order = order + 100; + if (isAppMenuActionSubmenu(appMenuItem)) { + return getAppMenuSubmenuWithAssignedOrder(appMenuItem, order); + } + return { + ...appMenuItem, + order: appMenuItem.order ?? order, + }; + }); +} diff --git a/packages/kbn-discover-utils/src/components/app_menu/types.ts b/packages/kbn-discover-utils/src/components/app_menu/types.ts new file mode 100644 index 0000000000000..d5cd1bde16be7 --- /dev/null +++ b/packages/kbn-discover-utils/src/components/app_menu/types.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React from 'react'; +import type { TopNavMenuData } from '@kbn/navigation-plugin/public'; +import type { EuiIconType } from '@elastic/eui/src/components/icon/icon'; + +export interface AppMenuControlOnClickParams { + anchorElement: HTMLElement; + onFinishAction: () => void; +} + +export type AppMenuControlProps = Pick< + TopNavMenuData, + 'testId' | 'isLoading' | 'label' | 'description' | 'disableButton' | 'href' | 'tooltip' +> & { + onClick: + | ((params: AppMenuControlOnClickParams) => Promise) + | ((params: AppMenuControlOnClickParams) => React.ReactNode | void) + | undefined; +}; + +export type AppMenuControlWithIconProps = AppMenuControlProps & { + iconType: EuiIconType; +}; + +interface ControlWithOptionalIcon { + iconType?: EuiIconType; +} + +export enum AppMenuActionId { + new = 'new', + open = 'open', + share = 'share', + alerts = 'alerts', + inspect = 'inspect', + createRule = 'createRule', + manageRulesAndConnectors = 'manageRulesAndConnectors', +} + +export enum AppMenuActionType { + primary = 'primary', + secondary = 'secondary', + custom = 'custom', + submenuHorizontalRule = 'submenuHorizontalRule', +} + +export interface AppMenuActionBase { + readonly id: AppMenuActionId | string; + readonly order?: number | undefined; +} + +/** + * A secondary menu action + */ +export interface AppMenuActionSecondary extends AppMenuActionBase { + readonly type: AppMenuActionType.secondary; + readonly controlProps: AppMenuControlProps; +} + +/** + * A secondary submenu action + */ +export interface AppMenuSubmenuActionSecondary + extends Omit { + readonly controlProps: AppMenuControlProps & ControlWithOptionalIcon; +} + +/** + * A custom menu action + */ +export interface AppMenuActionCustom extends AppMenuActionBase { + readonly type: AppMenuActionType.custom; + readonly controlProps: AppMenuControlProps; +} + +/** + * A custom submenu action + */ +export interface AppMenuSubmenuActionCustom extends Omit { + readonly controlProps: AppMenuControlProps & ControlWithOptionalIcon; +} + +/** + * A primary menu action (with icon only) + */ +export interface AppMenuActionPrimary extends AppMenuActionBase { + readonly type: AppMenuActionType.primary; + readonly controlProps: AppMenuControlWithIconProps; +} + +/** + * A horizontal rule between menu items + */ +export interface AppMenuSubmenuHorizontalRule extends AppMenuActionBase { + readonly type: AppMenuActionType.submenuHorizontalRule; + readonly testId?: TopNavMenuData['testId']; +} + +/** + * A menu action which opens a submenu with more actions + */ +export interface AppMenuActionSubmenuBase + extends AppMenuActionBase { + readonly type: T extends AppMenuActionSecondary + ? AppMenuActionType.secondary + : AppMenuActionType.custom; + readonly label: TopNavMenuData['label']; + readonly description?: TopNavMenuData['description']; + readonly testId?: TopNavMenuData['testId']; + readonly actions: T extends AppMenuActionSecondary + ? Array< + AppMenuSubmenuActionSecondary | AppMenuSubmenuActionCustom | AppMenuSubmenuHorizontalRule + > + : Array; +} + +/** + * A menu action which opens a submenu with more secondary actions + */ +export type AppMenuActionSubmenuSecondary = AppMenuActionSubmenuBase; +/** + * A menu action which opens a submenu with more custom actions + */ +export type AppMenuActionSubmenuCustom = AppMenuActionSubmenuBase; + +/** + * A primary menu item can only have an icon + */ +export type AppMenuItemPrimary = AppMenuActionPrimary; +/** + * A secondary menu item can have only a label or a submenu + */ +export type AppMenuItemSecondary = AppMenuActionSecondary | AppMenuActionSubmenuSecondary; +/** + * A custom menu item can have only a label or a submenu + */ +export type AppMenuItemCustom = AppMenuActionCustom | AppMenuActionSubmenuCustom; +/** + * A menu item can be primary, secondary or custom + */ +export type AppMenuItem = AppMenuItemPrimary | AppMenuItemSecondary | AppMenuItemCustom; diff --git a/packages/kbn-discover-utils/src/index.ts b/packages/kbn-discover-utils/src/index.ts index 8fe9a9418c9fe..243dd05774448 100644 --- a/packages/kbn-discover-utils/src/index.ts +++ b/packages/kbn-discover-utils/src/index.ts @@ -14,3 +14,4 @@ export * from './utils'; export * from './data_types'; export * from './components/custom_control_columns'; +export { AppMenuRegistry } from './components/app_menu/app_menu_registry'; diff --git a/packages/kbn-discover-utils/src/types.ts b/packages/kbn-discover-utils/src/types.ts index 63297edfe7643..2c298da999490 100644 --- a/packages/kbn-discover-utils/src/types.ts +++ b/packages/kbn-discover-utils/src/types.ts @@ -17,6 +17,8 @@ export type { RowControlProps, RowControlRowProps, } from './components/custom_control_columns/types'; +export type * from './components/app_menu/types'; +export { AppMenuActionId, AppMenuActionType } from './components/app_menu/types'; type DiscoverSearchHit = SearchHit>; diff --git a/packages/kbn-discover-utils/tsconfig.json b/packages/kbn-discover-utils/tsconfig.json index 865603e379eca..c26624d139dec 100644 --- a/packages/kbn-discover-utils/tsconfig.json +++ b/packages/kbn-discover-utils/tsconfig.json @@ -27,7 +27,8 @@ "@kbn/core-ui-settings-browser", "@kbn/expressions-plugin", "@kbn/logs-data-access-plugin", - "@kbn/ui-theme", - "@kbn/i18n-react" + "@kbn/i18n-react", + "@kbn/navigation-plugin", + "@kbn/ui-theme" ] } diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index 2e9183ed18b56..1294f72b9f208 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -283,9 +283,6 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D base: `${ELASTIC_WEBSITE_URL}guide/en/logstash/${DOC_LINK_VERSION}`, inputElasticAgent: `${ELASTIC_WEBSITE_URL}guide/en/logstash/${DOC_LINK_VERSION}/plugins-inputs-elastic_agent.html`, }, - functionbeat: { - base: `${ELASTIC_WEBSITE_URL}guide/en/beats/functionbeat/${DOC_LINK_VERSION}`, - }, winlogbeat: { base: `${ELASTIC_WEBSITE_URL}guide/en/beats/winlogbeat/${DOC_LINK_VERSION}`, }, diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index 9a41985460b61..0bfb1c69fd6bb 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -240,9 +240,6 @@ export interface DocLinks { readonly base: string; readonly inputElasticAgent: string; }; - readonly functionbeat: { - readonly base: string; - }; readonly winlogbeat: { readonly base: string; }; diff --git a/packages/kbn-es-types/src/search.ts b/packages/kbn-es-types/src/search.ts index 4c780fb2a2986..87f9dd15517c9 100644 --- a/packages/kbn-es-types/src/search.ts +++ b/packages/kbn-es-types/src/search.ts @@ -682,6 +682,7 @@ export interface ESQLSearchResponse { all_columns?: ESQLColumn[]; values: ESQLRow[]; took?: number; + _clusters?: estypes.ClusterStatistics; } export interface ESQLSearchParams { diff --git a/packages/kbn-eslint-plugin-eslint/rules/no_deprecated_authz_config.js b/packages/kbn-eslint-plugin-eslint/rules/no_deprecated_authz_config.js index 0f0b8759b4a82..8661c5e1c52d6 100644 --- a/packages/kbn-eslint-plugin-eslint/rules/no_deprecated_authz_config.js +++ b/packages/kbn-eslint-plugin-eslint/rules/no_deprecated_authz_config.js @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -const routeMethods = ['get', 'put', 'delete', 'post']; +const routeMethods = ['get', 'put', 'delete', 'post', 'patch']; const ACCESS_TAG_PREFIX = 'access:'; const isStringLiteral = (el) => el.type === 'Literal' && typeof el.value === 'string'; diff --git a/packages/kbn-esql-ast/src/builder/builder.ts b/packages/kbn-esql-ast/src/builder/builder.ts index 26b64a6312ee4..894ab99e5b3e8 100644 --- a/packages/kbn-esql-ast/src/builder/builder.ts +++ b/packages/kbn-esql-ast/src/builder/builder.ts @@ -11,15 +11,22 @@ import { ESQLAstComment, + ESQLAstCommentMultiLine, + ESQLAstCommentSingleLine, ESQLAstQueryExpression, ESQLColumn, ESQLCommand, ESQLCommandOption, ESQLDecimalLiteral, + ESQLIdentifier, ESQLInlineCast, ESQLIntegerLiteral, ESQLList, ESQLLocation, + ESQLNamedParamLiteral, + ESQLParam, + ESQLPositionalParamLiteral, + ESQLOrderExpression, ESQLSource, } from '../types'; import { AstNodeParserFields, AstNodeTemplate, PartialFields } from './types'; @@ -63,17 +70,17 @@ export namespace Builder { }; }; - export const comment = ( - subtype: ESQLAstComment['subtype'], + export const comment = ( + subtype: S, text: string, - location: ESQLLocation - ): ESQLAstComment => { + location?: ESQLLocation + ): S extends 'multi-line' ? ESQLAstCommentMultiLine : ESQLAstCommentSingleLine => { return { type: 'comment', subtype, text, location, - }; + } as S extends 'multi-line' ? ESQLAstCommentMultiLine : ESQLAstCommentSingleLine; }; export namespace expression { @@ -130,6 +137,20 @@ export namespace Builder { }; }; + export const order = ( + operand: ESQLColumn, + template: Omit, 'name' | 'args'>, + fromParser?: Partial + ): ESQLOrderExpression => { + return { + ...template, + ...Builder.parserFields(fromParser), + name: '', + args: [operand], + type: 'order', + }; + }; + export const inlineCast = ( template: Omit, 'name'>, fromParser?: Partial @@ -173,4 +194,65 @@ export namespace Builder { }; } } + + export const identifier = ( + template: AstNodeTemplate, + fromParser?: Partial + ): ESQLIdentifier => { + return { + ...template, + ...Builder.parserFields(fromParser), + type: 'identifier', + }; + }; + + export namespace param { + export const unnamed = (fromParser?: Partial): ESQLParam => { + const node = { + ...Builder.parserFields(fromParser), + name: '', + value: '', + paramType: 'unnamed', + type: 'literal', + literalType: 'param', + }; + + return node as ESQLParam; + }; + + export const named = ( + template: Omit, 'name' | 'literalType' | 'paramType'>, + fromParser?: Partial + ): ESQLNamedParamLiteral => { + const node: ESQLNamedParamLiteral = { + ...template, + ...Builder.parserFields(fromParser), + name: '', + type: 'literal', + literalType: 'param', + paramType: 'named', + }; + + return node; + }; + + export const positional = ( + template: Omit< + AstNodeTemplate, + 'name' | 'literalType' | 'paramType' + >, + fromParser?: Partial + ): ESQLPositionalParamLiteral => { + const node: ESQLPositionalParamLiteral = { + ...template, + ...Builder.parserFields(fromParser), + name: '', + type: 'literal', + literalType: 'param', + paramType: 'positional', + }; + + return node; + }; + } } diff --git a/packages/kbn-esql-ast/src/mutate/commands/from/metadata.ts b/packages/kbn-esql-ast/src/mutate/commands/from/metadata.ts index 7f08fa2a5e946..5160ab65954cb 100644 --- a/packages/kbn-esql-ast/src/mutate/commands/from/metadata.ts +++ b/packages/kbn-esql-ast/src/mutate/commands/from/metadata.ts @@ -106,7 +106,7 @@ export const removeByPredicate = ( option.args.splice(index, 1); if (option.args.length === 0) { - generic.removeCommandOption(ast, option); + generic.commands.options.remove(ast, option); } return tuple; @@ -148,16 +148,16 @@ export const insert = ( fieldName: string | string[], index: number = -1 ): [column: ESQLColumn, option: ESQLCommandOption] | undefined => { - let option = generic.findCommandOptionByName(ast, 'from', 'metadata'); + let option = generic.commands.options.findByName(ast, 'from', 'metadata'); if (!option) { - const command = generic.findCommandByName(ast, 'from'); + const command = generic.commands.findByName(ast, 'from'); if (!command) { return; } - option = generic.appendCommandOption(command, 'metadata'); + option = generic.commands.options.append(command, 'metadata'); } const parts: string[] = typeof fieldName === 'string' ? [fieldName] : fieldName; @@ -189,7 +189,7 @@ export const upsert = ( fieldName: string | string[], index: number = -1 ): [column: ESQLColumn, option: ESQLCommandOption] | undefined => { - const option = generic.findCommandOptionByName(ast, 'from', 'metadata'); + const option = generic.commands.options.findByName(ast, 'from', 'metadata'); if (option) { const parts = Array.isArray(fieldName) ? fieldName : [fieldName]; diff --git a/packages/kbn-esql-ast/src/mutate/commands/from/sources.ts b/packages/kbn-esql-ast/src/mutate/commands/from/sources.ts index da67500b5b0bd..c10096cec38d9 100644 --- a/packages/kbn-esql-ast/src/mutate/commands/from/sources.ts +++ b/packages/kbn-esql-ast/src/mutate/commands/from/sources.ts @@ -67,7 +67,7 @@ export const remove = ( return undefined; } - const success = generic.removeCommandArgument(ast, node); + const success = generic.commands.args.remove(ast, node); return success ? node : undefined; }; @@ -78,7 +78,7 @@ export const insert = ( clusterName?: string, index: number = -1 ): ESQLSource | undefined => { - const command = generic.findCommandByName(ast, 'from'); + const command = generic.commands.findByName(ast, 'from'); if (!command) { return; @@ -87,7 +87,7 @@ export const insert = ( const source = Builder.expression.indexSource(indexName, clusterName); if (index === -1) { - generic.appendCommandArgument(command, source); + generic.commands.args.append(command, source); } else { command.args.splice(index, 0, source); } diff --git a/packages/kbn-esql-ast/src/mutate/commands/index.ts b/packages/kbn-esql-ast/src/mutate/commands/index.ts index 0a779292e6eca..9e2599c493459 100644 --- a/packages/kbn-esql-ast/src/mutate/commands/index.ts +++ b/packages/kbn-esql-ast/src/mutate/commands/index.ts @@ -9,5 +9,6 @@ import * as from from './from'; import * as limit from './limit'; +import * as sort from './sort'; -export { from, limit }; +export { from, limit, sort }; diff --git a/packages/kbn-esql-ast/src/mutate/commands/limit/index.ts b/packages/kbn-esql-ast/src/mutate/commands/limit/index.ts index 937538e848328..f181a1d5f0cd4 100644 --- a/packages/kbn-esql-ast/src/mutate/commands/limit/index.ts +++ b/packages/kbn-esql-ast/src/mutate/commands/limit/index.ts @@ -19,7 +19,7 @@ import { Predicate } from '../../types'; * @returns A collection of "LIMIT" commands. */ export const list = (ast: ESQLAstQueryExpression): IterableIterator => { - return generic.listCommands(ast, (cmd) => cmd.name === 'limit'); + return generic.commands.list(ast, (cmd) => cmd.name === 'limit'); }; /** @@ -55,13 +55,13 @@ export const find = ( * @returns The removed "LIMIT" command, if any. */ export const remove = (ast: ESQLAstQueryExpression, index: number = 0): ESQLCommand | undefined => { - const command = generic.findCommandByName(ast, 'limit', index); + const command = generic.commands.findByName(ast, 'limit', index); if (!command) { return; } - const success = generic.removeCommand(ast, command); + const success = !!generic.commands.remove(ast, command); if (!success) { return; @@ -128,7 +128,7 @@ export const upsert = ( args: [literal], }); - generic.appendCommand(ast, command); + generic.commands.append(ast, command); return command; }; diff --git a/packages/kbn-esql-ast/src/mutate/commands/sort/index.test.ts b/packages/kbn-esql-ast/src/mutate/commands/sort/index.test.ts new file mode 100644 index 0000000000000..d04f79b96541a --- /dev/null +++ b/packages/kbn-esql-ast/src/mutate/commands/sort/index.test.ts @@ -0,0 +1,527 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { parse } from '../../../parser'; +import * as commands from '..'; +import { BasicPrettyPrinter } from '../../../pretty_print'; +import { Builder } from '../../../builder'; + +describe('commands.sort', () => { + describe('.listCommands()', () => { + it('returns empty array, if there are no sort commands', () => { + const src = 'FROM index METADATA a'; + const { root } = parse(src); + const list = [...commands.sort.listCommands(root)]; + + expect(list.length).toBe(0); + }); + + it('returns all sort commands', () => { + const src = + 'FROM index | SORT a ASC, b DESC, c | LIMIT 123 | SORT d | EVAL 1 | SORT e NULLS FIRST, f NULLS LAST'; + const { root } = parse(src); + const list = [...commands.sort.listCommands(root)]; + + expect(list.length).toBe(3); + }); + + it('can skip given number of sort commands', () => { + const src = + 'FROM index | SORT a ASC, b DESC, c | LIMIT 123 | SORT d | EVAL 1 | SORT e NULLS FIRST, f NULLS LAST'; + const { root } = parse(src); + const list1 = [...commands.sort.listCommands(root, 1)]; + const list2 = [...commands.sort.listCommands(root, 2)]; + const list3 = [...commands.sort.listCommands(root, 3)]; + const list4 = [...commands.sort.listCommands(root, 111)]; + + expect(list1.length).toBe(2); + expect(list2.length).toBe(1); + expect(list3.length).toBe(0); + expect(list4.length).toBe(0); + }); + }); + + describe('.list()', () => { + it('returns empty array, if there are no sort commands', () => { + const src = 'FROM index METADATA a'; + const { root } = parse(src); + const list = [...commands.sort.list(root)]; + + expect(list.length).toBe(0); + }); + + it('returns a single column expression', () => { + const src = 'FROM index | SORT a'; + const { root } = parse(src); + const list = [...commands.sort.list(root)].map(([node]) => node); + + expect(list.length).toBe(1); + expect(list[0]).toMatchObject({ + type: 'column', + name: 'a', + }); + }); + + it('returns a single order expression', () => { + const src = 'FROM index | SORT a ASC'; + const { root } = parse(src); + const list = [...commands.sort.list(root)].map(([node]) => node); + + expect(list.length).toBe(1); + expect(list[0]).toMatchObject({ + type: 'order', + args: [ + { + type: 'column', + name: 'a', + }, + ], + }); + }); + + it('returns all sort command expressions', () => { + const src = + 'FROM index | SORT a ASC, b DESC, c | LIMIT 123 | SORT d | EVAL 1 | SORT e NULLS FIRST, f NULLS LAST'; + const { root } = parse(src); + const list = [...commands.sort.list(root)].map(([node]) => node); + + expect(list).toMatchObject([ + { + type: 'order', + args: [ + { + type: 'column', + name: 'a', + }, + ], + }, + { + type: 'order', + args: [ + { + type: 'column', + name: 'b', + }, + ], + }, + { + type: 'column', + name: 'c', + }, + { + type: 'column', + name: 'd', + }, + { + type: 'order', + args: [ + { + type: 'column', + name: 'e', + }, + ], + }, + { + type: 'order', + args: [ + { + type: 'column', + name: 'f', + }, + ], + }, + ]); + }); + + it('can skip one order expression', () => { + const src = 'FROM index | SORT b DESC, a ASC'; + const { root } = parse(src); + const list = [...commands.sort.list(root, 1)].map(([node]) => node); + + expect(list.length).toBe(1); + expect(list[0]).toMatchObject({ + type: 'order', + args: [ + { + type: 'column', + name: 'a', + }, + ], + }); + }); + }); + + describe('.find()', () => { + it('returns undefined if sort expression is not found', () => { + const src = 'FROM index | WHERE a = b | LIMIT 123'; + const { root } = parse(src); + const node = commands.sort.find(root, 'abc'); + + expect(node).toBe(undefined); + }); + + it('can find a single sort expression', () => { + const src = 'FROM index | SORT a'; + const { root } = parse(src); + const [node] = commands.sort.find(root, 'a')!; + + expect(node).toMatchObject({ + type: 'column', + name: 'a', + }); + }); + + it('can find a single sort (order) expression', () => { + const src = 'FROM index | SORT b ASC'; + const { root } = parse(src); + const [node] = commands.sort.find(root, 'b')!; + + expect(node).toMatchObject({ + type: 'order', + args: [ + { + type: 'column', + name: 'b', + }, + ], + }); + }); + + it('can find a column and specific order expressions among other such expressions', () => { + const src = + 'FROM index | SORT a, b ASC | STATS agg() | SORT c DESC, d, e NULLS FIRST | LIMIT 10'; + const { root } = parse(src); + const [node1] = commands.sort.find(root, 'b')!; + const [node2] = commands.sort.find(root, 'd')!; + + expect(node1).toMatchObject({ + type: 'order', + args: [ + { + type: 'column', + name: 'b', + }, + ], + }); + expect(node2).toMatchObject({ + type: 'column', + name: 'd', + }); + }); + + it('can select second order expression with the same name', () => { + const src = 'FROM index | SORT b ASC | STATS agg() | SORT b DESC'; + const { root } = parse(src); + const [node] = commands.sort.find(root, 'b', 1)!; + + expect(node).toMatchObject({ + type: 'order', + order: 'DESC', + args: [ + { + type: 'column', + name: 'b', + }, + ], + }); + }); + + it('can find multipart columns', () => { + const src = 'FROM index | SORT hello, b.a ASC, a.b, c, c.d | STATS agg() | SORT b DESC'; + const { root } = parse(src); + const [node1] = commands.sort.find(root, ['b', 'a'])!; + const [node2] = commands.sort.find(root, ['a', 'b'])!; + + expect(node1).toMatchObject({ + type: 'order', + order: 'ASC', + args: [ + { + type: 'column', + parts: ['b', 'a'], + }, + ], + }); + expect(node2).toMatchObject({ + type: 'column', + parts: ['a', 'b'], + }); + }); + + it('returns the parent sort command of the found order expression', () => { + const src = 'FROM index | SORT hello, b.a ASC, a.b, c, c.d | STATS agg() | SORT b DESC'; + const { root } = parse(src); + const [node1, command1] = commands.sort.find(root, ['b', 'a'])!; + const [node2, command2] = commands.sort.find(root, ['a', 'b'])!; + + expect(command1).toBe(command2); + expect(!!command1.args.find((arg) => arg === node1)).toBe(true); + expect(!!command2.args.find((arg) => arg === node2)).toBe(true); + }); + }); + + describe('.remove()', () => { + it('can remove a column from a list', () => { + const src1 = 'FROM a, b, c | SORT a, b, c'; + const { root } = parse(src1); + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM a, b, c | SORT a, b, c'); + + commands.sort.remove(root, 'b'); + + const src3 = BasicPrettyPrinter.print(root); + + expect(src3).toBe('FROM a, b, c | SORT a, c'); + }); + + it('can remove an order expression from a list', () => { + const src1 = 'FROM a, b, c | SORT a, b ASC, c'; + const { root } = parse(src1); + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM a, b, c | SORT a, b ASC, c'); + + commands.sort.remove(root, 'b'); + + const src3 = BasicPrettyPrinter.print(root); + + expect(src3).toBe('FROM a, b, c | SORT a, c'); + }); + + it('does nothing if column does not exist', () => { + const src1 = 'FROM a, b, c | SORT a, c'; + const { root } = parse(src1); + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM a, b, c | SORT a, c'); + + commands.sort.remove(root, 'b'); + commands.sort.remove(root, 'd'); + + const src3 = BasicPrettyPrinter.print(root); + + expect(src3).toBe('FROM a, b, c | SORT a, c'); + }); + + it('can remove the sort expression at specific index', () => { + const src1 = 'FROM index | SORT a, b, c | LIMIT 1 | SORT a, b, c | LIMIT 2 | SORT a, b, c'; + const { root } = parse(src1); + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe( + 'FROM index | SORT a, b, c | LIMIT 1 | SORT a, b, c | LIMIT 2 | SORT a, b, c' + ); + + commands.sort.remove(root, 'a', 1); + commands.sort.remove(root, 'c', 1); + commands.sort.remove(root, 'b', 2); + + const src3 = BasicPrettyPrinter.print(root); + + expect(src3).toBe('FROM index | SORT a, b, c | LIMIT 1 | SORT b | LIMIT 2 | SORT a, c'); + }); + + it('removes SORT command, if it is left empty', () => { + const src1 = 'FROM index | SORT a, b, c | LIMIT 1 | SORT a, b, c | LIMIT 2 | SORT a, b, c'; + const { root } = parse(src1); + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe( + 'FROM index | SORT a, b, c | LIMIT 1 | SORT a, b, c | LIMIT 2 | SORT a, b, c' + ); + + commands.sort.remove(root, 'c', 1); + commands.sort.remove(root, 'b', 1); + commands.sort.remove(root, 'a', 1); + + const src3 = BasicPrettyPrinter.print(root); + + expect(src3).toBe('FROM index | SORT a, b, c | LIMIT 1 | LIMIT 2 | SORT a, b, c'); + }); + + it('can remove by matching parts', () => { + const src1 = 'FROM a, b, c | SORT a, b.c, d.e NULLS FIRST, e'; + const { root } = parse(src1); + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM a, b, c | SORT a, b.c, d.e NULLS FIRST, e'); + + commands.sort.remove(root, ['b', 'c']); + + const src3 = BasicPrettyPrinter.print(root); + + expect(src3).toBe('FROM a, b, c | SORT a, d.e NULLS FIRST, e'); + + commands.sort.remove(root, ['d', 'e']); + + const src4 = BasicPrettyPrinter.print(root); + + expect(src4).toBe('FROM a, b, c | SORT a, e'); + }); + }); + + describe('.insertIntoCommand()', () => { + it('can insert a sorting condition into the first existing SORT command', () => { + const src1 = 'FROM a, b, c | SORT s1, s2'; + const { root } = parse(src1); + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM a, b, c | SORT s1, s2'); + + const command = commands.sort.getCommand(root)!; + commands.sort.insertIntoCommand(command, 's3'); + + const src3 = BasicPrettyPrinter.print(root); + + expect(src3).toBe('FROM a, b, c | SORT s1, s2, s3'); + }); + + it('can prepend a sorting condition with options into the first existing SORT command', () => { + const src1 = 'FROM a, b, c | SORT s1, s2'; + const { root } = parse(src1); + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM a, b, c | SORT s1, s2'); + + const command = commands.sort.getCommand(root)!; + commands.sort.insertIntoCommand( + command, + { parts: ['address', 'street🙃'], order: 'ASC', nulls: 'NULLS FIRST' }, + 0 + ); + + const src3 = BasicPrettyPrinter.print(root); + + expect(src3).toBe('FROM a, b, c | SORT address.`street🙃` ASC NULLS FIRST, s1, s2'); + }); + + it('can insert a sorting condition into specific sorting command into specific position', () => { + const src1 = 'FROM a, b, c | SORT a1, a2 | SORT b1, /* HERE */ b3 | SORT c1, c2'; + const { root } = parse(src1); + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM a, b, c | SORT a1, a2 | SORT b1, b3 | SORT c1, c2'); + + const command = commands.sort.getCommand(root, 1)!; + commands.sort.insertIntoCommand(command, 'b2', 1); + + const src3 = BasicPrettyPrinter.print(root); + + expect(src3).toBe('FROM a, b, c | SORT a1, a2 | SORT b1, b2, b3 | SORT c1, c2'); + }); + }); + + describe('.insertExpression()', () => { + it('can insert a sorting condition into the first existing SORT command', () => { + const src1 = 'FROM a, b, c | SORT s1, s2'; + const { root } = parse(src1); + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM a, b, c | SORT s1, s2'); + + commands.sort.insertExpression(root, 's3'); + + const src3 = BasicPrettyPrinter.print(root); + + expect(src3).toBe('FROM a, b, c | SORT s1, s2, s3'); + }); + + it('can insert a sorting condition into specific sorting command into specific position', () => { + const src1 = 'FROM a, b, c | SORT a1, a2 | SORT b1, /* HERE */ b3 | SORT c1, c2'; + const { root } = parse(src1); + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM a, b, c | SORT a1, a2 | SORT b1, b3 | SORT c1, c2'); + + commands.sort.insertExpression(root, 'b2', 1, 1); + + const src3 = BasicPrettyPrinter.print(root); + + expect(src3).toBe('FROM a, b, c | SORT a1, a2 | SORT b1, b2, b3 | SORT c1, c2'); + }); + + it('when no positional arguments are provided append the column to the first SORT command', () => { + const src1 = 'FROM a, b, c | SORT a1, a2 | SORT b1, b2 | SORT c1, c2'; + const { root } = parse(src1); + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM a, b, c | SORT a1, a2 | SORT b1, b2 | SORT c1, c2'); + + commands.sort.insertExpression(root, 'a3'); + + const src3 = BasicPrettyPrinter.print(root); + + expect(src3).toBe('FROM a, b, c | SORT a1, a2, a3 | SORT b1, b2 | SORT c1, c2'); + }); + + it('when no SORT command found, inserts a new SORT command', () => { + const src1 = 'FROM a, b, c | LIMIT 10'; + const { root } = parse(src1); + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM a, b, c | LIMIT 10'); + + commands.sort.insertExpression(root, ['i18n', 'language', 'locale']); + + const src3 = BasicPrettyPrinter.print(root); + + expect(src3).toBe('FROM a, b, c | LIMIT 10 | SORT i18n.language.locale'); + }); + + it('can change the sorting order', () => { + const src1 = 'FROM a, b, c | SORT a ASC'; + const { root } = parse(src1); + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM a, b, c | SORT a ASC'); + + commands.sort.insertExpression(root, { parts: 'a', order: 'DESC' }); + commands.sort.remove(root, 'a', 0); + + const src3 = BasicPrettyPrinter.print(root); + + expect(src3).toBe('FROM a, b, c | SORT a DESC'); + }); + }); + + describe('.insertCommand()', () => { + it('can append a new SORT command', () => { + const src1 = 'FROM a, b, c | SORT s1, s2'; + const { root } = parse(src1); + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM a, b, c | SORT s1, s2'); + + commands.sort.insertCommand(root, 's3'); + + const src3 = BasicPrettyPrinter.print(root); + + expect(src3).toBe('FROM a, b, c | SORT s1, s2 | SORT s3'); + }); + + it('can insert a SORT command before a LIMIT command (and add a comment)', () => { + const src1 = 'FROM a, b, c | LIMIT 10'; + const { root } = parse(src1); + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM a, b, c | LIMIT 10'); + + const [_, column] = commands.sort.insertCommand(root, 'b', 1); + + column.formatting = { + right: [Builder.comment('multi-line', ' we sort by "b" ')], + }; + + const src3 = BasicPrettyPrinter.print(root); + + expect(src3).toBe('FROM a, b, c | SORT b /* we sort by "b" */ | LIMIT 10'); + }); + }); +}); diff --git a/packages/kbn-esql-ast/src/mutate/commands/sort/index.ts b/packages/kbn-esql-ast/src/mutate/commands/sort/index.ts new file mode 100644 index 0000000000000..d2b2c7cd5f3d4 --- /dev/null +++ b/packages/kbn-esql-ast/src/mutate/commands/sort/index.ts @@ -0,0 +1,313 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { Builder } from '../../../builder'; +import { + ESQLAstQueryExpression, + ESQLColumn, + ESQLCommand, + ESQLOrderExpression, +} from '../../../types'; +import { Visitor } from '../../../visitor'; +import { Predicate } from '../../types'; +import * as util from '../../util'; +import * as generic from '../../generic'; + +export type SortExpression = ESQLOrderExpression | ESQLColumn; + +/** + * This "template" allows the developer to easily specify a new sort expression + * AST node, for example: + * + * ```ts + * // as a simple string + * 'column_name' + * + * // column with nested fields + * ['column_name', 'nested_field'] + * + * // as an object with additional options + * { parts: 'column_name', order: 'ASC', nulls: 'NULLS FIRST' } + * { parts: ['column_name', 'nested_field'], order: 'DESC', nulls: 'NULLS LAST' } + * ``` + */ +export type NewSortExpressionTemplate = + | string + | string[] + | { + parts: string | string[]; + order?: ESQLOrderExpression['order']; + nulls?: ESQLOrderExpression['nulls']; + }; + +const createSortExpression = ( + template: string | string[] | NewSortExpressionTemplate +): SortExpression => { + const column = Builder.expression.column({ + parts: + typeof template === 'string' + ? [template] + : Array.isArray(template) + ? template + : typeof template.parts === 'string' + ? [template.parts] + : template.parts, + }); + + if (typeof template === 'string' || Array.isArray(template)) { + return column; + } + + const order = Builder.expression.order(column, { + order: template.order ?? '', + nulls: template.nulls ?? '', + }); + + return order; +}; + +/** + * Iterates through all sort commands starting from the beginning of the query. + * You can specify the `skip` parameter to skip a given number of sort commands. + * + * @param ast The root of the AST. + * @param skip Number of sort commands to skip. + * @returns Iterator through all sort commands. + */ +export const listCommands = ( + ast: ESQLAstQueryExpression, + skip: number = 0 +): IterableIterator => { + return new Visitor() + .on('visitSortCommand', function* (ctx): IterableIterator { + if (skip) { + skip--; + } else { + yield ctx.node; + } + }) + .on('visitCommand', function* (): IterableIterator {}) + .on('visitQuery', function* (ctx): IterableIterator { + for (const command of ctx.visitCommands()) { + yield* command; + } + }) + .visitQuery(ast); +}; + +/** + * Returns the Nth SORT command found in the query. + * + * @param ast The root of the AST. + * @param index The index (N) of the sort command to return. + * @returns The sort command found in the AST, if any. + */ +export const getCommand = ( + ast: ESQLAstQueryExpression, + index: number = 0 +): ESQLCommand | undefined => { + for (const command of listCommands(ast, index)) { + return command; + } +}; + +/** + * Returns an iterator for all sort expressions (columns and order expressions) + * in the query. You can specify the `skip` parameter to skip a given number of + * expressions. + * + * @param ast The root of the AST. + * @param skip Number of sort expressions to skip. + * @returns Iterator through sort expressions (columns and order expressions). + */ +export const list = ( + ast: ESQLAstQueryExpression, + skip: number = 0 +): IterableIterator<[sortExpression: SortExpression, sortCommand: ESQLCommand]> => { + return new Visitor() + .on('visitSortCommand', function* (ctx): IterableIterator<[SortExpression, ESQLCommand]> { + for (const argument of ctx.arguments()) { + if (argument.type === 'order' || argument.type === 'column') { + if (skip) { + skip--; + } else { + yield [argument, ctx.node]; + } + } + } + }) + .on('visitCommand', function* (): IterableIterator<[SortExpression, ESQLCommand]> {}) + .on('visitQuery', function* (ctx): IterableIterator<[SortExpression, ESQLCommand]> { + for (const command of ctx.visitCommands()) { + yield* command; + } + }) + .visitQuery(ast); +}; + +/** + * Finds the Nts sort expression that matches the predicate. + * + * @param ast The root of the AST. + * @param predicate A function that returns true if the sort expression matches + * the predicate. + * @param index The index of the sort expression to return. If not specified, + * the first sort expression that matches the predicate will be returned. + * @returns The sort expressions and sort command 2-tuple that matches the + * predicate, if any. + */ +export const findByPredicate = ( + ast: ESQLAstQueryExpression, + predicate: Predicate<[sortExpression: SortExpression, sortCommand: ESQLCommand]>, + index?: number +): [sortExpression: SortExpression, sortCommand: ESQLCommand] | undefined => { + return util.findByPredicate(list(ast, index), predicate); +}; + +/** + * Finds the Nth sort expression that matches the sort expression by column + * name. The `parts` argument allows to specify an array of nested field names. + * + * @param ast The root of the AST. + * @param parts A string or an array of strings representing the column name. + * @returns The sort expressions and sort command 2-tuple that matches the + * predicate, if any. + */ +export const find = ( + ast: ESQLAstQueryExpression, + parts: string | string[], + index: number = 0 +): [sortExpression: SortExpression, sortCommand: ESQLCommand] | undefined => { + const arrParts = typeof parts === 'string' ? [parts] : parts; + + return findByPredicate(ast, ([node]) => { + let isMatch = false; + if (node.type === 'column') { + isMatch = util.cmpArr(node.parts, arrParts); + } else if (node.type === 'order') { + const columnParts = (node.args[0] as ESQLColumn)?.parts; + + if (Array.isArray(columnParts)) { + isMatch = util.cmpArr(columnParts, arrParts); + } + } + + if (isMatch) { + index--; + if (index < 0) { + return true; + } + } + + return false; + }); +}; + +/** + * Removes the Nth sort expression that matches the sort expression by column + * name. The `parts` argument allows to specify an array of nested field names. + * + * @param ast The root of the AST. + * @param parts A string or an array of strings representing the column name. + * @param index The index of the sort expression to remove. + * @returns The sort expressions and sort command 2-tuple that was removed, if any. + */ +export const remove = ( + ast: ESQLAstQueryExpression, + parts: string | string[], + index?: number +): [sortExpression: SortExpression, sortCommand: ESQLCommand] | undefined => { + const tuple = find(ast, parts, index); + + if (!tuple) { + return undefined; + } + + const [node] = tuple; + const cmd = generic.commands.args.remove(ast, node); + + if (cmd) { + if (!cmd.args.length) { + generic.commands.remove(ast, cmd); + } + } + + return cmd ? tuple : undefined; +}; + +/** + * Inserts a new sort expression into the specified SORT command at the + * specified argument position. + * + * @param sortCommand The SORT command to insert the new sort expression into. + * @param template The sort expression template. + * @param index Argument position in the command argument list. + * @returns The inserted sort expression. + */ +export const insertIntoCommand = ( + sortCommand: ESQLCommand, + template: NewSortExpressionTemplate, + index?: number +): SortExpression => { + const expression = createSortExpression(template); + + generic.commands.args.insert(sortCommand, expression, index); + + return expression; +}; + +/** + * Creates a new sort expression node and inserts it into the specified SORT + * command at the specified argument position. If not sort command is found, a + * new one is created and appended to the end of the query. + * + * @param ast The root AST node. + * @param parts ES|QL column name parts. + * @param index The new column name position in command argument list. + * @param sortCommandIndex The index of the SORT command in the AST. E.g. 0 is the + * first SORT command in the AST. + * @returns The inserted column AST node. + */ +export const insertExpression = ( + ast: ESQLAstQueryExpression, + template: NewSortExpressionTemplate, + index: number = -1, + sortCommandIndex: number = 0 +): SortExpression => { + let command: ESQLCommand | undefined = getCommand(ast, sortCommandIndex); + + if (!command) { + command = Builder.command({ name: 'sort' }); + generic.commands.append(ast, command); + } + + return insertIntoCommand(command, template, index); +}; + +/** + * Inserts a new SORT command with a single sort expression as its sole argument. + * You can specify the position to insert the command at. + * + * @param ast The root of the AST. + * @param template The sort expression template. + * @param index The position to insert the sort expression at. + * @returns The inserted sort expression and the command it was inserted into. + */ +export const insertCommand = ( + ast: ESQLAstQueryExpression, + template: NewSortExpressionTemplate, + index: number = -1 +): [ESQLCommand, SortExpression] => { + const expression = createSortExpression(template); + const command = Builder.command({ name: 'sort', args: [expression] }); + + generic.commands.insert(ast, command, index); + + return [command, expression]; +}; diff --git a/packages/kbn-esql-ast/src/mutate/generic.ts b/packages/kbn-esql-ast/src/mutate/generic.ts deleted file mode 100644 index f27b0e2ae399f..0000000000000 --- a/packages/kbn-esql-ast/src/mutate/generic.ts +++ /dev/null @@ -1,287 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { isOptionNode } from '../ast/util'; -import { Builder } from '../builder'; -import { - ESQLAstQueryExpression, - ESQLCommand, - ESQLCommandOption, - ESQLProperNode, - ESQLSingleAstItem, -} from '../types'; -import { Visitor } from '../visitor'; -import { Predicate } from './types'; - -/** - * Returns an iterator for all command AST nodes in the query. If a predicate is - * provided, only commands that satisfy the predicate will be returned. - * - * @param ast Root AST node to search for commands. - * @param predicate Optional predicate to filter commands. - * @returns A list of commands found in the AST. - */ -export const listCommands = ( - ast: ESQLAstQueryExpression, - predicate?: Predicate -): IterableIterator => { - return new Visitor() - .on('visitQuery', function* (ctx): IterableIterator { - for (const cmd of ctx.commands()) { - if (!predicate || predicate(cmd)) { - yield cmd; - } - } - }) - .visitQuery(ast); -}; - -/** - * Returns the first command AST node at a given index in the query that - * satisfies the predicate. If no index is provided, the first command found - * will be returned. - * - * @param ast Root AST node to search for commands. - * @param predicate Optional predicate to filter commands. - * @param index The index of the command to return. - * @returns The command found in the AST, if any. - */ -export const findCommand = ( - ast: ESQLAstQueryExpression, - predicate?: Predicate, - index: number = 0 -): ESQLCommand | undefined => { - for (const cmd of listCommands(ast, predicate)) { - if (!index) { - return cmd; - } - - index--; - } - - return undefined; -}; - -/** - * Returns the first command option AST node that satisfies the predicate. - * - * @param command The command AST node to search for options. - * @param predicate The predicate to filter options. - * @returns The option found in the command, if any. - */ -export const findCommandOption = ( - command: ESQLCommand, - predicate: Predicate -): ESQLCommandOption | undefined => { - return new Visitor() - .on('visitCommand', (ctx): ESQLCommandOption | undefined => { - for (const opt of ctx.options()) { - if (predicate(opt)) { - return opt; - } - } - - return undefined; - }) - .visitCommand(command); -}; - -/** - * Returns the first command AST node at a given index with a given name in the - * query. If no index is provided, the first command found will be returned. - * - * @param ast Root AST node to search for commands. - * @param commandName The name of the command to find. - * @param index The index of the command to return. - * @returns The command found in the AST, if any. - */ -export const findCommandByName = ( - ast: ESQLAstQueryExpression, - commandName: string, - index: number = 0 -): ESQLCommand | undefined => { - return findCommand(ast, (cmd) => cmd.name === commandName, index); -}; - -/** - * Returns the first command option AST node with a given name in the query. - * - * @param ast The root AST node to search for command options. - * @param commandName Command name to search for. - * @param optionName Option name to search for. - * @returns The option found in the command, if any. - */ -export const findCommandOptionByName = ( - ast: ESQLAstQueryExpression, - commandName: string, - optionName: string -): ESQLCommandOption | undefined => { - const command = findCommand(ast, (cmd) => cmd.name === commandName); - - if (!command) { - return undefined; - } - - return findCommandOption(command, (opt) => opt.name === optionName); -}; - -/** - * Adds a new command to the query AST node. - * - * @param ast The root AST node to append the command to. - * @param command The command AST node to append. - */ -export const appendCommand = (ast: ESQLAstQueryExpression, command: ESQLCommand): void => { - ast.commands.push(command); -}; - -/** - * Inserts a command option into the command's arguments list. The option can - * be specified as a string or an AST node. - * - * @param command The command AST node to insert the option into. - * @param option The option to insert. - * @returns The inserted option. - */ -export const appendCommandOption = ( - command: ESQLCommand, - option: string | ESQLCommandOption -): ESQLCommandOption => { - if (typeof option === 'string') { - option = Builder.option({ name: option }); - } - - command.args.push(option); - - return option; -}; - -export const appendCommandArgument = ( - command: ESQLCommand, - expression: ESQLSingleAstItem -): number => { - if (expression.type === 'option') { - command.args.push(expression); - return command.args.length - 1; - } - - const index = command.args.findIndex((arg) => isOptionNode(arg)); - - if (index > -1) { - command.args.splice(index, 0, expression); - return index; - } - - command.args.push(expression); - return command.args.length - 1; -}; - -export const removeCommand = (ast: ESQLAstQueryExpression, command: ESQLCommand): boolean => { - const cmds = ast.commands; - const length = cmds.length; - - for (let i = 0; i < length; i++) { - if (cmds[i] === command) { - cmds.splice(i, 1); - return true; - } - } - - return false; -}; - -/** - * Removes the first command option from the command's arguments list that - * satisfies the predicate. - * - * @param command The command AST node to remove the option from. - * @param predicate The predicate to filter options. - * @returns The removed option, if any. - */ -export const removeCommandOption = ( - ast: ESQLAstQueryExpression, - option: ESQLCommandOption -): boolean => { - return new Visitor() - .on('visitCommandOption', (ctx): boolean => { - return ctx.node === option; - }) - .on('visitCommand', (ctx): boolean => { - let target: undefined | ESQLCommandOption; - - for (const opt of ctx.options()) { - if (opt === option) { - target = opt; - break; - } - } - - if (!target) { - return false; - } - - const index = ctx.node.args.indexOf(target); - - if (index === -1) { - return false; - } - - ctx.node.args.splice(index, 1); - - return true; - }) - .on('visitQuery', (ctx): boolean => { - for (const success of ctx.visitCommands()) { - if (success) { - return true; - } - } - - return false; - }) - .visitQuery(ast); -}; - -/** - * Searches all command arguments in the query AST node and removes the node - * from the command's arguments list. - * - * @param ast The root AST node to search for command arguments. - * @param node The argument AST node to remove. - * @returns Returns true if the argument was removed, false otherwise. - */ -export const removeCommandArgument = ( - ast: ESQLAstQueryExpression, - node: ESQLProperNode -): boolean => { - return new Visitor() - .on('visitCommand', (ctx): boolean => { - const args = ctx.node.args; - const length = args.length; - - for (let i = 0; i < length; i++) { - if (args[i] === node) { - args.splice(i, 1); - return true; - } - } - - return false; - }) - .on('visitQuery', (ctx): boolean => { - for (const success of ctx.visitCommands()) { - if (success) { - return true; - } - } - - return false; - }) - .visitQuery(ast); -}; diff --git a/packages/kbn-esql-ast/src/mutate/generic/commands/args/index.test.ts b/packages/kbn-esql-ast/src/mutate/generic/commands/args/index.test.ts new file mode 100644 index 0000000000000..e687c4528dd7d --- /dev/null +++ b/packages/kbn-esql-ast/src/mutate/generic/commands/args/index.test.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { Builder } from '../../../../builder'; +import { parse } from '../../../../parser'; +import { BasicPrettyPrinter } from '../../../../pretty_print'; +import * as generic from '../..'; + +describe('generic.commands.args', () => { + describe('.insert()', () => { + it('can insert at the end of the list', () => { + const src = 'FROM index | LIMIT 10'; + const { root } = parse(src); + const command = generic.commands.findByName(root, 'from', 0); + + generic.commands.args.insert( + command!, + Builder.expression.source({ name: 'test', sourceType: 'index' }), + 123 + ); + + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM index, test | LIMIT 10'); + }); + + it('can insert at the beginning of the list', () => { + const src = 'FROM index | LIMIT 10'; + const { root } = parse(src); + const command = generic.commands.findByName(root, 'from', 0); + + generic.commands.args.insert( + command!, + Builder.expression.source({ name: 'test', sourceType: 'index' }), + 0 + ); + + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM test, index | LIMIT 10'); + }); + + it('can insert in the middle of the list', () => { + const src = 'FROM index1, index2 | LIMIT 10'; + const { root } = parse(src); + const command = generic.commands.findByName(root, 'from', 0); + + generic.commands.args.insert( + command!, + Builder.expression.source({ name: 'test', sourceType: 'index' }), + 1 + ); + + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM index1, test, index2 | LIMIT 10'); + }); + + describe('with option present', () => { + it('can insert at the end of the list', () => { + const src = 'FROM index METADATA _id | LIMIT 10'; + const { root } = parse(src); + const command = generic.commands.findByName(root, 'from', 0); + + generic.commands.args.insert( + command!, + Builder.expression.source({ name: 'test', sourceType: 'index' }), + 123 + ); + + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM index, test METADATA _id | LIMIT 10'); + }); + + it('can insert at the beginning of the list', () => { + const src = 'FROM index METADATA _id | LIMIT 10'; + const { root } = parse(src); + const command = generic.commands.findByName(root, 'from', 0); + + generic.commands.args.insert( + command!, + Builder.expression.source({ name: 'test', sourceType: 'index' }), + 0 + ); + + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM test, index METADATA _id | LIMIT 10'); + }); + + it('can insert in the middle of the list', () => { + const src = 'FROM index1, index2 METADATA _id | LIMIT 10'; + const { root } = parse(src); + const command = generic.commands.findByName(root, 'from', 0); + + generic.commands.args.insert( + command!, + Builder.expression.source({ name: 'test', sourceType: 'index' }), + 1 + ); + + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM index1, test, index2 METADATA _id | LIMIT 10'); + }); + }); + }); + + describe('.append()', () => { + it('can append and argument', () => { + const src = 'FROM index METADATA _id | LIMIT 10'; + const { root } = parse(src); + const command = generic.commands.findByName(root, 'from', 0); + + generic.commands.args.append( + command!, + Builder.expression.source({ name: 'test', sourceType: 'index' }) + ); + + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM index, test METADATA _id | LIMIT 10'); + }); + }); +}); diff --git a/packages/kbn-esql-ast/src/mutate/generic/commands/args/index.ts b/packages/kbn-esql-ast/src/mutate/generic/commands/args/index.ts new file mode 100644 index 0000000000000..7072c38a5f1a8 --- /dev/null +++ b/packages/kbn-esql-ast/src/mutate/generic/commands/args/index.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { isOptionNode } from '../../../../ast/util'; +import { + ESQLAstQueryExpression, + ESQLCommand, + ESQLProperNode, + ESQLSingleAstItem, +} from '../../../../types'; +import { Visitor } from '../../../../visitor'; + +export const insert = ( + command: ESQLCommand, + expression: ESQLSingleAstItem, + index: number = -1 +): number => { + if (expression.type === 'option') { + command.args.push(expression); + return command.args.length - 1; + } + + let mainArgumentCount = command.args.findIndex((arg) => isOptionNode(arg)); + + if (mainArgumentCount < 0) { + mainArgumentCount = command.args.length; + } + if (index === -1) { + index = mainArgumentCount; + } + if (index > mainArgumentCount) { + index = mainArgumentCount; + } + + command.args.splice(index, 0, expression); + + return mainArgumentCount + 1; +}; + +export const append = (command: ESQLCommand, expression: ESQLSingleAstItem): number => { + return insert(command, expression, -1); +}; + +/** + * Searches all command arguments in the query AST node and removes the node + * from the command's arguments list. + * + * @param ast The root AST node to search for command arguments. + * @param node The argument AST node to remove. + * @returns Returns the command that the argument was removed from, if any. + */ +export const remove = ( + ast: ESQLAstQueryExpression, + node: ESQLProperNode +): ESQLCommand | undefined => { + return new Visitor() + .on('visitCommand', (ctx): ESQLCommand | undefined => { + const args = ctx.node.args; + const length = args.length; + + for (let i = 0; i < length; i++) { + if (args[i] === node) { + args.splice(i, 1); + return ctx.node; + } + } + + return undefined; + }) + .on('visitQuery', (ctx): ESQLCommand | undefined => { + for (const cmd of ctx.visitCommands()) { + if (cmd) { + return cmd; + } + } + + return undefined; + }) + .visitQuery(ast); +}; diff --git a/packages/kbn-esql-ast/src/mutate/generic.test.ts b/packages/kbn-esql-ast/src/mutate/generic/commands/index.test.ts similarity index 54% rename from packages/kbn-esql-ast/src/mutate/generic.test.ts rename to packages/kbn-esql-ast/src/mutate/generic/commands/index.test.ts index 0109ff838ffda..b35d0b6415247 100644 --- a/packages/kbn-esql-ast/src/mutate/generic.test.ts +++ b/packages/kbn-esql-ast/src/mutate/generic/commands/index.test.ts @@ -7,26 +7,26 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { parse } from '../parser'; -import { BasicPrettyPrinter } from '../pretty_print'; -import * as generic from './generic'; +import { parse } from '../../../parser'; +import { BasicPrettyPrinter } from '../../../pretty_print'; +import * as generic from '..'; -describe('generic', () => { - describe('.listCommands()', () => { +describe('generic.commands', () => { + describe('.list()', () => { it('lists all commands', () => { const src = 'FROM index | WHERE a == b | LIMIT 123'; const { root } = parse(src); - const commands = [...generic.listCommands(root)].map((cmd) => cmd.name); + const commands = [...generic.commands.list(root)].map((cmd) => cmd.name); expect(commands).toEqual(['from', 'where', 'limit']); }); }); - describe('.findCommand()', () => { + describe('.find()', () => { it('can the first command', () => { const src = 'FROM index | WHERE a == b | LIMIT 123'; const { root } = parse(src); - const command = generic.findCommand(root, (cmd) => cmd.name === 'from'); + const command = generic.commands.find(root, (cmd) => cmd.name === 'from'); expect(command).toMatchObject({ type: 'command', @@ -42,7 +42,7 @@ describe('generic', () => { it('can the last command', () => { const src = 'FROM index | WHERE a == b | LIMIT 123'; const { root } = parse(src); - const command = generic.findCommand(root, (cmd) => cmd.name === 'limit'); + const command = generic.commands.find(root, (cmd) => cmd.name === 'limit'); expect(command).toMatchObject({ type: 'command', @@ -58,7 +58,7 @@ describe('generic', () => { it('find the specific of multiple commands', () => { const src = 'FROM index | WHERE a == b | LIMIT 1 | LIMIT 2 | LIMIT 3'; const { root } = parse(src); - const command = generic.findCommand( + const command = generic.commands.find( root, (cmd) => cmd.name === 'limit' && (cmd.args?.[0] as any).value === 2 ); @@ -76,34 +76,13 @@ describe('generic', () => { }); }); - describe('.findCommandOptionByName()', () => { - it('can the find a command option', () => { - const src = 'FROM index METADATA _score'; - const { root } = parse(src); - const option = generic.findCommandOptionByName(root, 'from', 'metadata'); - - expect(option).toMatchObject({ - type: 'option', - name: 'metadata', - }); - }); - - it('returns undefined if there is no option', () => { - const src = 'FROM index'; - const { root } = parse(src); - const option = generic.findCommandOptionByName(root, 'from', 'metadata'); - - expect(option).toBe(undefined); - }); - }); - - describe('.removeCommand()', () => { + describe('.remove()', () => { it('can remove the last command', () => { const src = 'FROM index | LIMIT 10'; const { root } = parse(src); - const command = generic.findCommandByName(root, 'limit', 0); + const command = generic.commands.findByName(root, 'limit', 0); - generic.removeCommand(root, command!); + generic.commands.remove(root, command!); const src2 = BasicPrettyPrinter.print(root); @@ -113,9 +92,9 @@ describe('generic', () => { it('can remove the second command out of 3 with the same name', () => { const src = 'FROM index | LIMIT 1 | LIMIT 2 | LIMIT 3'; const { root } = parse(src); - const command = generic.findCommandByName(root, 'limit', 1); + const command = generic.commands.findByName(root, 'limit', 1); - generic.removeCommand(root, command!); + generic.commands.remove(root, command!); const src2 = BasicPrettyPrinter.print(root); @@ -125,29 +104,15 @@ describe('generic', () => { it('can remove all commands', () => { const src = 'FROM index | WHERE a == b | LIMIT 123'; const { root } = parse(src); - const cmd1 = generic.findCommandByName(root, 'where'); - const cmd2 = generic.findCommandByName(root, 'limit'); - const cmd3 = generic.findCommandByName(root, 'from'); + const cmd1 = generic.commands.findByName(root, 'where'); + const cmd2 = generic.commands.findByName(root, 'limit'); + const cmd3 = generic.commands.findByName(root, 'from'); - generic.removeCommand(root, cmd1!); - generic.removeCommand(root, cmd2!); - generic.removeCommand(root, cmd3!); + generic.commands.remove(root, cmd1!); + generic.commands.remove(root, cmd2!); + generic.commands.remove(root, cmd3!); expect(root.commands.length).toBe(0); }); }); - - describe('.removeCommandOption()', () => { - it('can remove existing command option', () => { - const src = 'FROM index METADATA _score'; - const { root } = parse(src); - const option = generic.findCommandOptionByName(root, 'from', 'metadata'); - - generic.removeCommandOption(root, option!); - - const src2 = BasicPrettyPrinter.print(root); - - expect(src2).toBe('FROM index'); - }); - }); }); diff --git a/packages/kbn-esql-ast/src/mutate/generic/commands/index.ts b/packages/kbn-esql-ast/src/mutate/generic/commands/index.ts new file mode 100644 index 0000000000000..0582bb592edb8 --- /dev/null +++ b/packages/kbn-esql-ast/src/mutate/generic/commands/index.ts @@ -0,0 +1,131 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { ESQLAstQueryExpression, ESQLCommand } from '../../../types'; +import { Visitor } from '../../../visitor'; +import { Predicate } from '../../types'; + +export * as args from './args'; +export * as options from './options'; + +/** + * Returns an iterator for all command AST nodes in the query. If a predicate is + * provided, only commands that satisfy the predicate will be returned. + * + * @param ast Root AST node to search for commands. + * @param predicate Optional predicate to filter commands. + * @returns A list of commands found in the AST. + */ +export const list = ( + ast: ESQLAstQueryExpression, + predicate?: Predicate +): IterableIterator => { + return new Visitor() + .on('visitQuery', function* (ctx): IterableIterator { + for (const cmd of ctx.commands()) { + if (!predicate || predicate(cmd)) { + yield cmd; + } + } + }) + .visitQuery(ast); +}; + +/** + * Returns the first command AST node at a given index in the query that + * satisfies the predicate. If no index is provided, the first command found + * will be returned. + * + * @param ast Root AST node to search for commands. + * @param predicate Optional predicate to filter commands. + * @param index The index of the command to return. + * @returns The command found in the AST, if any. + */ +export const find = ( + ast: ESQLAstQueryExpression, + predicate?: Predicate, + index: number = 0 +): ESQLCommand | undefined => { + for (const cmd of list(ast, predicate)) { + if (!index) { + return cmd; + } + + index--; + } + + return undefined; +}; + +/** + * Returns the first command AST node at a given index with a given name in the + * query. If no index is provided, the first command found will be returned. + * + * @param ast Root AST node to search for commands. + * @param commandName The name of the command to find. + * @param index The index of the command to return. + * @returns The command found in the AST, if any. + */ +export const findByName = ( + ast: ESQLAstQueryExpression, + commandName: string, + index: number = 0 +): ESQLCommand | undefined => { + return find(ast, (cmd) => cmd.name === commandName, index); +}; + +/** + * Inserts a new command into the query AST node at the specified index. If the + * `index` is out of bounds, the command will be appended to the end of the + * command list. + * + * @param ast The root AST node. + * @param command The command AST node to insert. + * @param index The index to insert the command at. + * @returns The index the command was inserted at. + */ +export const insert = ( + ast: ESQLAstQueryExpression, + command: ESQLCommand, + index: number = Infinity +): number => { + const commands = ast.commands; + + if (index > commands.length || index < 0) { + index = commands.length; + } + + commands.splice(index, 0, command); + + return index; +}; + +/** + * Adds a new command to the query AST node. + * + * @param ast The root AST node to append the command to. + * @param command The command AST node to append. + */ +export const append = (ast: ESQLAstQueryExpression, command: ESQLCommand): void => { + ast.commands.push(command); +}; + +export const remove = (ast: ESQLAstQueryExpression, command: ESQLCommand): boolean => { + const cmds = ast.commands; + const length = cmds.length; + + for (let i = 0; i < length; i++) { + if (cmds[i] === command) { + cmds.splice(i, 1); + return true; + } + } + + return false; +}; diff --git a/packages/kbn-esql-ast/src/mutate/generic/commands/options/index.test.ts b/packages/kbn-esql-ast/src/mutate/generic/commands/options/index.test.ts new file mode 100644 index 0000000000000..00c3ee90eccdd --- /dev/null +++ b/packages/kbn-esql-ast/src/mutate/generic/commands/options/index.test.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { parse } from '../../../../parser'; +import { BasicPrettyPrinter } from '../../../../pretty_print'; +import * as generic from '../..'; + +describe('generic.commands.options', () => { + describe('.findByName()', () => { + it('can the find a command option', () => { + const src = 'FROM index METADATA _score'; + const { root } = parse(src); + const option = generic.commands.options.findByName(root, 'from', 'metadata'); + + expect(option).toMatchObject({ + type: 'option', + name: 'metadata', + }); + }); + + it('returns undefined if there is no option', () => { + const src = 'FROM index'; + const { root } = parse(src); + const option = generic.commands.options.findByName(root, 'from', 'metadata'); + + expect(option).toBe(undefined); + }); + }); + + describe('.remove()', () => { + it('can remove existing command option', () => { + const src = 'FROM index METADATA _score'; + const { root } = parse(src); + const option = generic.commands.options.findByName(root, 'from', 'metadata'); + + generic.commands.options.remove(root, option!); + + const src2 = BasicPrettyPrinter.print(root); + + expect(src2).toBe('FROM index'); + }); + }); +}); diff --git a/packages/kbn-esql-ast/src/mutate/generic/commands/options/index.ts b/packages/kbn-esql-ast/src/mutate/generic/commands/options/index.ts new file mode 100644 index 0000000000000..b9b2bac452e31 --- /dev/null +++ b/packages/kbn-esql-ast/src/mutate/generic/commands/options/index.ts @@ -0,0 +1,130 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { Builder } from '../../../../builder'; +import { ESQLAstQueryExpression, ESQLCommand, ESQLCommandOption } from '../../../../types'; +import { Visitor } from '../../../../visitor'; +import { Predicate } from '../../../types'; +import * as commands from '..'; + +/** + * Returns the first command option AST node that satisfies the predicate. + * + * @param command The command AST node to search for options. + * @param predicate The predicate to filter options. + * @returns The option found in the command, if any. + */ +export const find = ( + command: ESQLCommand, + predicate: Predicate +): ESQLCommandOption | undefined => { + return new Visitor() + .on('visitCommand', (ctx): ESQLCommandOption | undefined => { + for (const opt of ctx.options()) { + if (predicate(opt)) { + return opt; + } + } + + return undefined; + }) + .visitCommand(command); +}; + +/** + * Returns the first command option AST node with a given name in the query. + * + * @param ast The root AST node to search for command options. + * @param commandName Command name to search for. + * @param optionName Option name to search for. + * @returns The option found in the command, if any. + */ +export const findByName = ( + ast: ESQLAstQueryExpression, + commandName: string, + optionName: string +): ESQLCommandOption | undefined => { + const command = commands.find(ast, (cmd) => cmd.name === commandName); + + if (!command) { + return undefined; + } + + return find(command, (opt) => opt.name === optionName); +}; + +/** + * Inserts a command option into the command's arguments list. The option can + * be specified as a string or an AST node. + * + * @param command The command AST node to insert the option into. + * @param option The option to insert. + * @returns The inserted option. + */ +export const append = ( + command: ESQLCommand, + option: string | ESQLCommandOption +): ESQLCommandOption => { + if (typeof option === 'string') { + option = Builder.option({ name: option }); + } + + command.args.push(option); + + return option; +}; + +/** + * Removes the first command option from the command's arguments list that + * satisfies the predicate. + * + * @param command The command AST node to remove the option from. + * @param predicate The predicate to filter options. + * @returns The removed option, if any. + */ +export const remove = (ast: ESQLAstQueryExpression, option: ESQLCommandOption): boolean => { + return new Visitor() + .on('visitCommandOption', (ctx): boolean => { + return ctx.node === option; + }) + .on('visitCommand', (ctx): boolean => { + let target: undefined | ESQLCommandOption; + + for (const opt of ctx.options()) { + if (opt === option) { + target = opt; + break; + } + } + + if (!target) { + return false; + } + + const index = ctx.node.args.indexOf(target); + + if (index === -1) { + return false; + } + + ctx.node.args.splice(index, 1); + + return true; + }) + .on('visitQuery', (ctx): boolean => { + for (const success of ctx.visitCommands()) { + if (success) { + return true; + } + } + + return false; + }) + .visitQuery(ast); +}; diff --git a/packages/kbn-esql-ast/src/mutate/generic/index.ts b/packages/kbn-esql-ast/src/mutate/generic/index.ts new file mode 100644 index 0000000000000..e7f26b9340af7 --- /dev/null +++ b/packages/kbn-esql-ast/src/mutate/generic/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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export * as commands from './commands'; diff --git a/packages/kbn-esql-ast/src/parser/__tests__/function.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/function.test.ts index 9d822f78f9333..d05ed36204b17 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/function.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/function.test.ts @@ -69,6 +69,103 @@ describe('function AST nodes', () => { }, ]); }); + + it('parses out function name as identifier node', () => { + const query = 'ROW fn(1, 2, 3)'; + const { ast, errors } = parse(query); + + expect(errors.length).toBe(0); + expect(ast).toMatchObject([ + { + type: 'command', + name: 'row', + args: [ + { + type: 'function', + name: 'fn', + operator: { + type: 'identifier', + name: 'fn', + }, + }, + ], + }, + ]); + }); + + it('parses out function name as named param', () => { + const query = 'ROW ?insert_here(1, 2, 3)'; + const { ast, errors } = parse(query); + + expect(errors.length).toBe(0); + expect(ast).toMatchObject([ + { + type: 'command', + name: 'row', + args: [ + { + type: 'function', + name: '?insert_here', + operator: { + type: 'literal', + literalType: 'param', + paramType: 'named', + value: 'insert_here', + }, + }, + ], + }, + ]); + }); + + it('parses out function name as unnamed param', () => { + const query = 'ROW ?(1, 2, 3)'; + const { ast, errors } = parse(query); + + expect(errors.length).toBe(0); + expect(ast).toMatchObject([ + { + type: 'command', + name: 'row', + args: [ + { + type: 'function', + name: '?', + operator: { + type: 'literal', + literalType: 'param', + paramType: 'unnamed', + }, + }, + ], + }, + ]); + }); + + it('parses out function name as positional param', () => { + const query = 'ROW ?30035(1, 2, 3)'; + const { ast, errors } = parse(query); + + expect(errors.length).toBe(0); + expect(ast).toMatchObject([ + { + type: 'command', + name: 'row', + args: [ + { + type: 'function', + name: '?30035', + operator: { + type: 'literal', + literalType: 'param', + paramType: 'positional', + value: 30035, + }, + }, + ], + }, + ]); + }); }); describe('"unary-expression"', () => { diff --git a/packages/kbn-esql-ast/src/parser/factories.ts b/packages/kbn-esql-ast/src/parser/factories.ts index 0fffb3a970e4c..b575447f7e744 100644 --- a/packages/kbn-esql-ast/src/parser/factories.ts +++ b/packages/kbn-esql-ast/src/parser/factories.ts @@ -11,7 +11,13 @@ * In case of changes in the grammar, this script should be updated: esql_update_ast_script.js */ -import type { Token, ParserRuleContext, TerminalNode, RecognitionException } from 'antlr4'; +import type { + Token, + ParserRuleContext, + TerminalNode, + RecognitionException, + ParseTree, +} from 'antlr4'; import { IndexPatternContext, QualifiedNameContext, @@ -21,6 +27,10 @@ import { type IntegerValueContext, type QualifiedIntegerLiteralContext, QualifiedNamePatternContext, + FunctionContext, + IdentifierContext, + InputParamContext, + InputNamedOrPositionalParamContext, } from '../antlr/esql_parser'; import { DOUBLE_TICKS_REGEX, SINGLE_BACKTICK, TICKS_REGEX } from './constants'; import type { @@ -42,6 +52,8 @@ import type { ESQLNumericLiteral, ESQLOrderExpression, InlineCastingType, + ESQLFunctionCallExpression, + ESQLIdentifier, } from '../types'; import { parseIdentifier, getPosition } from './helpers'; import { Builder, type AstNodeParserFields } from '../builder'; @@ -201,22 +213,71 @@ export function createFunction( return node; } +export const createFunctionCall = (ctx: FunctionContext): ESQLFunctionCallExpression => { + const functionExpressionCtx = ctx.functionExpression(); + const functionName = functionExpressionCtx.functionName(); + const node: ESQLFunctionCallExpression = { + type: 'function', + subtype: 'variadic-call', + name: functionName.getText().toLowerCase(), + text: ctx.getText(), + location: getPosition(ctx.start, ctx.stop), + args: [], + incomplete: Boolean(ctx.exception), + }; + + const identifierOrParameter = functionName.identifierOrParameter(); + if (identifierOrParameter) { + const identifier = identifierOrParameter.identifier(); + if (identifier) { + node.operator = createIdentifier(identifier); + } else { + const parameter = identifierOrParameter.parameter(); + if (parameter) { + node.operator = createParam(parameter); + } + } + } + + return node; +}; + +const createIdentifier = (identifier: IdentifierContext): ESQLIdentifier => { + return Builder.identifier( + { name: identifier.getText().toLowerCase() }, + createParserFields(identifier) + ); +}; + +export const createParam = (ctx: ParseTree) => { + if (ctx instanceof InputParamContext) { + return Builder.param.unnamed(createParserFields(ctx)); + } else if (ctx instanceof InputNamedOrPositionalParamContext) { + const text = ctx.getText(); + const value = text.slice(1); + const valueAsNumber = Number(value); + const isPositional = String(valueAsNumber) === value; + const parserFields = createParserFields(ctx); + + if (isPositional) { + return Builder.param.positional({ value: valueAsNumber }, parserFields); + } else { + return Builder.param.named({ value }, parserFields); + } + } +}; + export const createOrderExpression = ( ctx: ParserRuleContext, - arg: ESQLAstItem, + arg: ESQLColumn, order: ESQLOrderExpression['order'], nulls: ESQLOrderExpression['nulls'] ) => { - const node: ESQLOrderExpression = { - type: 'order', - name: '', - order, - nulls, - args: [arg], - text: ctx.getText(), - location: getPosition(ctx.start, ctx.stop), - incomplete: Boolean(ctx.exception), - }; + const node = Builder.expression.order( + arg as ESQLColumn, + { order, nulls }, + createParserFields(ctx) + ); return node; }; diff --git a/packages/kbn-esql-ast/src/parser/formatting.ts b/packages/kbn-esql-ast/src/parser/formatting.ts index 492e8a76ddeac..f7c556da63008 100644 --- a/packages/kbn-esql-ast/src/parser/formatting.ts +++ b/packages/kbn-esql-ast/src/parser/formatting.ts @@ -173,6 +173,10 @@ const attachCommentDecoration = ( ) => { const commentConsumesWholeLine = !comment.hasContentToLeft && !comment.hasContentToRight; + if (!comment.node.location) { + return; + } + if (commentConsumesWholeLine) { const node = Visitor.findNodeAtOrAfter(ast, comment.node.location.max - 1); diff --git a/packages/kbn-esql-ast/src/parser/walkers.ts b/packages/kbn-esql-ast/src/parser/walkers.ts index df10161f68bf8..60d69a17bb1c7 100644 --- a/packages/kbn-esql-ast/src/parser/walkers.ts +++ b/packages/kbn-esql-ast/src/parser/walkers.ts @@ -60,8 +60,6 @@ import { type ValueExpressionContext, ValueExpressionDefaultContext, InlineCastContext, - InputNamedOrPositionalParamContext, - InputParamContext, IndexPatternContext, InlinestatsCommandContext, } from '../antlr/esql_parser'; @@ -86,8 +84,9 @@ import { createInlineCast, createUnknownItem, createOrderExpression, + createFunctionCall, + createParam, } from './factories'; -import { getPosition } from './helpers'; import { ESQLLiteral, @@ -97,9 +96,6 @@ import { ESQLAstItem, ESQLAstField, ESQLInlineCast, - ESQLUnnamedParamLiteral, - ESQLPositionalParamLiteral, - ESQLNamedParamLiteral, ESQLOrderExpression, } from '../types'; import { firstItem, lastItem } from '../visitor/utils'; @@ -390,50 +386,8 @@ function getConstant(ctx: ConstantContext): ESQLAstItem { const values: ESQLLiteral[] = []; for (const child of ctx.children) { - if (child instanceof InputParamContext) { - const literal: ESQLUnnamedParamLiteral = { - type: 'literal', - literalType: 'param', - paramType: 'unnamed', - text: ctx.getText(), - name: '', - value: '', - location: getPosition(ctx.start, ctx.stop), - incomplete: Boolean(ctx.exception), - }; - values.push(literal); - } else if (child instanceof InputNamedOrPositionalParamContext) { - const text = child.getText(); - const value = text.slice(1); - const valueAsNumber = Number(value); - const isPositional = String(valueAsNumber) === value; - - if (isPositional) { - const literal: ESQLPositionalParamLiteral = { - type: 'literal', - literalType: 'param', - paramType: 'positional', - value: valueAsNumber, - text, - name: '', - location: getPosition(ctx.start, ctx.stop), - incomplete: Boolean(ctx.exception), - }; - values.push(literal); - } else { - const literal: ESQLNamedParamLiteral = { - type: 'literal', - literalType: 'param', - paramType: 'named', - value, - text, - name: '', - location: getPosition(ctx.start, ctx.stop), - incomplete: Boolean(ctx.exception), - }; - values.push(literal); - } - } + const param = createParam(child); + if (param) values.push(param); } return values; @@ -478,15 +432,7 @@ export function visitPrimaryExpression(ctx: PrimaryExpressionContext): ESQLAstIt } if (ctx instanceof FunctionContext) { const functionExpressionCtx = ctx.functionExpression(); - const functionNameContext = functionExpressionCtx.functionName().MATCH() - ? functionExpressionCtx.functionName().MATCH() - : functionExpressionCtx.functionName().identifierOrParameter(); - const fn = createFunction( - functionNameContext.getText().toLowerCase(), - ctx, - undefined, - 'variadic-call' - ); + const fn = createFunctionCall(ctx); const asteriskArg = functionExpressionCtx.ASTERISK() ? createColumnStar(functionExpressionCtx.ASTERISK()!) : undefined; @@ -671,7 +617,7 @@ const visitOrderExpression = (ctx: OrderExpressionContext): ESQLOrderExpression return arg; } - return createOrderExpression(ctx, arg, order, nulls); + return createOrderExpression(ctx, arg as ESQLColumn, order, nulls); }; export function visitOrderExpressions( diff --git a/packages/kbn-esql-ast/src/types.ts b/packages/kbn-esql-ast/src/types.ts index 0df75ee2e8f24..ea76fc3e0b9a4 100644 --- a/packages/kbn-esql-ast/src/types.ts +++ b/packages/kbn-esql-ast/src/types.ts @@ -26,6 +26,7 @@ export type ESQLSingleAstItem = | ESQLTimeInterval | ESQLList | ESQLLiteral + | ESQLIdentifier | ESQLCommandMode | ESQLInlineCast | ESQLOrderExpression @@ -132,6 +133,11 @@ export interface ESQLFunction< */ subtype?: Subtype; + /** + * A node representing the function or operator being called. + */ + operator?: ESQLIdentifier | ESQLParamLiteral; + args: ESQLAstItem[]; } @@ -363,6 +369,10 @@ export interface ESQLNamedParamLiteral extends ESQLParamLiteral<'named'> { value: string; } +export interface ESQLIdentifier extends ESQLAstBaseItem { + type: 'identifier'; +} + export const isESQLNamedParamLiteral = (node: ESQLAstItem): node is ESQLNamedParamLiteral => isESQLAstBaseItem(node) && (node as ESQLNamedParamLiteral).literalType === 'param' && @@ -376,6 +386,11 @@ export interface ESQLPositionalParamLiteral extends ESQLParamLiteral<'positional value: number; } +export type ESQLParam = + | ESQLUnnamedParamLiteral + | ESQLNamedParamLiteral + | ESQLPositionalParamLiteral; + export interface ESQLMessage { type: 'error' | 'warning'; text: string; @@ -404,7 +419,7 @@ export interface ESQLAstGenericComment; diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.from.test.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.from.test.ts index fa2e81ded897e..571e7c295e14c 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.from.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.from.test.ts @@ -91,14 +91,12 @@ describe('autocomplete.suggest', () => { test('on SPACE after "METADATA" keyword suggests all metadata fields', async () => { const { assertSuggestions } = await setup(); - await assertSuggestions('from a, b [METADATA /]', metadataFields); await assertSuggestions('from a, b METADATA /', metadataFields); }); test('on SPACE after "METADATA" column suggests command and pipe operators', async () => { const { assertSuggestions } = await setup(); - await assertSuggestions('from a, b [metadata _index /]', [',', '| ']); await assertSuggestions('from a, b metadata _index /', [',', '| ']); await assertSuggestions('from a, b metadata _index, _source /', [',', '| ']); await assertSuggestions(`from a, b metadata ${METADATA_FIELDS.join(', ')} /`, ['| ']); @@ -107,7 +105,6 @@ describe('autocomplete.suggest', () => { test('filters out already used metadata fields', async () => { const { assertSuggestions } = await setup(); - await assertSuggestions('from a, b [metadata _index, /]', metadataFieldsAndIndex); await assertSuggestions('from a, b metadata _index, /', metadataFieldsAndIndex); }); }); diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.stats.test.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.stats.test.ts index b3884f5cb96be..829c12f7dabba 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.stats.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.stats.test.ts @@ -9,8 +9,8 @@ import { FieldType, FunctionReturnType } from '../../definitions/types'; import { ESQL_COMMON_NUMERIC_TYPES, ESQL_NUMBER_TYPES } from '../../shared/esql_types'; +import { getDateHistogramCompletionItem } from '../commands/stats/util'; import { allStarConstant } from '../complete_items'; -import { getAddDateHistogramSnippet } from '../factories'; import { roundParameterTypes } from './constants'; import { setup, @@ -71,7 +71,7 @@ describe('autocomplete.suggest', () => { test('on space after aggregate field', async () => { const { assertSuggestions } = await setup(); - await assertSuggestions('from a | stats a=min(b) /', ['BY $0', ',', '| ']); + await assertSuggestions('from a | stats a=min(b) /', ['BY ', ', ', '| ']); }); test('on space after aggregate field with comma', async () => { @@ -184,7 +184,7 @@ describe('autocomplete.suggest', () => { test('when typing right paren', async () => { const { assertSuggestions } = await setup(); - await assertSuggestions('from a | stats a = min(b)/ | sort b', ['BY $0', ',', '| ']); + await assertSuggestions('from a | stats a = min(b)/ | sort b', ['BY ', ', ', '| ']); }); test('increments suggested variable name counter', async () => { @@ -192,9 +192,8 @@ describe('autocomplete.suggest', () => { await assertSuggestions('from a | eval var0=round(b), var1=round(c) | stats /', [ 'var2 = ', + // TODO verify that this change is ok ...allAggFunctions, - 'var0', - 'var1', ...allEvaFunctions, ]); await assertSuggestions('from a | stats var0=min(b),var1=c,/', [ @@ -210,7 +209,7 @@ describe('autocomplete.suggest', () => { const { assertSuggestions } = await setup(); const expected = [ 'var0 = ', - getAddDateHistogramSnippet(), + getDateHistogramCompletionItem(), ...getFieldNamesByType('any').map((field) => `${field} `), ...allEvaFunctions, ...allGroupingFunctions, @@ -224,7 +223,7 @@ describe('autocomplete.suggest', () => { test('on space after grouping field', async () => { const { assertSuggestions } = await setup(); - await assertSuggestions('from a | stats a=c by d /', [',', '| ']); + await assertSuggestions('from a | stats a=c by d /', [', ', '| ']); }); test('after comma "," in grouping fields', async () => { @@ -233,7 +232,7 @@ describe('autocomplete.suggest', () => { const fields = getFieldNamesByType('any').map((field) => `${field} `); await assertSuggestions('from a | stats a=c by d, /', [ 'var0 = ', - getAddDateHistogramSnippet(), + getDateHistogramCompletionItem(), ...fields, ...allEvaFunctions, ...allGroupingFunctions, @@ -245,7 +244,7 @@ describe('autocomplete.suggest', () => { ]); await assertSuggestions('from a | stats avg(b) by c, /', [ 'var0 = ', - getAddDateHistogramSnippet(), + getDateHistogramCompletionItem(), ...fields, ...getFunctionSignaturesByReturnType('eval', 'any', { scalar: true }), ...allGroupingFunctions, @@ -262,17 +261,16 @@ describe('autocomplete.suggest', () => { ...getFunctionSignaturesByReturnType('eval', ['integer', 'double', 'long'], { scalar: true, }), - ...allGroupingFunctions, ]); await assertSuggestions('from a | stats avg(b) by var0 = /', [ - getAddDateHistogramSnippet(), + getDateHistogramCompletionItem(), ...getFieldNamesByType('any').map((field) => `${field} `), ...allEvaFunctions, ...allGroupingFunctions, ]); await assertSuggestions('from a | stats avg(b) by c, var0 = /', [ - getAddDateHistogramSnippet(), + getDateHistogramCompletionItem(), ...getFieldNamesByType('any').map((field) => `${field} `), ...allEvaFunctions, ...allGroupingFunctions, @@ -282,21 +280,17 @@ describe('autocomplete.suggest', () => { test('on space after expression right hand side operand', async () => { const { assertSuggestions } = await setup(); - await assertSuggestions('from a | stats avg(b) by doubleField % 2 /', [',', '| ']); - await assertSuggestions('from a | stats avg(b) by doubleField % 2 /', [',', '| '], { + await assertSuggestions('from a | stats avg(b) by doubleField % 2 /', [', ', '| '], { triggerCharacter: ' ', }); - await assertSuggestions( - 'from a | stats var0 = AVG(doubleField) BY var1 = BUCKET(dateField, 1 day)/', - [',', '| ', '+ $0', '- $0'] - ); await assertSuggestions( 'from a | stats var0 = AVG(doubleField) BY var1 = BUCKET(dateField, 1 day) /', - [',', '| ', '+ $0', '- $0'], + [', ', '| '], { triggerCharacter: ' ' } ); }); + test('on space within bucket()', async () => { const { assertSuggestions } = await setup(); await assertSuggestions('from a | stats avg(b) by BUCKET(/, 50, ?_tstart, ?_tend)', [ @@ -330,6 +324,29 @@ describe('autocomplete.suggest', () => { const suggestions = await suggest('from a | stats count(/)'); expect(suggestions).toContain(allStarConstant); }); + + describe('date histogram snippet', () => { + test('uses histogramBarTarget preference when available', async () => { + const { suggest } = await setup(); + const histogramBarTarget = Math.random() * 100; + const expectedCompletionItem = getDateHistogramCompletionItem(histogramBarTarget); + + const suggestions = await suggest('FROM a | STATS BY /', { + callbacks: { getPreferences: () => Promise.resolve({ histogramBarTarget }) }, + }); + + expect(suggestions).toContainEqual(expectedCompletionItem); + }); + + test('defaults gracefully', async () => { + const { suggest } = await setup(); + const expectedCompletionItem = getDateHistogramCompletionItem(); + + const suggestions = await suggest('FROM a | STATS BY /'); + + expect(suggestions).toContainEqual(expectedCompletionItem); + }); + }); }); }); }); diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts index 3234417c1f1a4..9964fc96d00ca 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts @@ -8,7 +8,7 @@ */ import { camelCase } from 'lodash'; -import { getAstAndSyntaxErrors } from '@kbn/esql-ast'; +import { parse } from '@kbn/esql-ast'; import { scalarFunctionDefinitions } from '../../definitions/generated/scalar_functions'; import { builtinFunctions } from '../../definitions/builtin'; import { aggregationFunctionDefinitions } from '../../definitions/generated/aggregation_functions'; @@ -177,7 +177,7 @@ export function getFunctionSignaturesByReturnType( ({ returnType }) => expectedReturnType.includes('any') || expectedReturnType.includes(returnType as string) ); - if (!filteredByReturnType.length) { + if (!filteredByReturnType.length && !expectedReturnType.includes('any')) { return false; } if (paramsTypes?.length) { @@ -312,7 +312,7 @@ export const setup = async (caret = '/') => { querySansCaret, pos, ctx, - getAstAndSyntaxErrors, + (_query: string | undefined) => parse(_query, { withFormatting: true }), opts.callbacks ?? callbacks ); }; diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/suggestions_in_comments.test.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/suggestions_in_comments.test.ts new file mode 100644 index 0000000000000..7f97409ea6341 --- /dev/null +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/suggestions_in_comments.test.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { setup } from './helpers'; + +describe('suggestions in comments', () => { + it('does not suggest in single-line comments', async () => { + const { assertSuggestions } = await setup('^'); + await assertSuggestions('FROM index | EVAL // hey there ^', []); + }); + + it('does not suggest in multi-line comments', async () => { + const { assertSuggestions } = await setup('^'); + await assertSuggestions('FROM index | EVAL /* ^ */', []); + await assertSuggestions('FROM index | EVAL /* (^) */', []); + }); + + it('does not suggest in incomplete multi-line comments', async () => { + const { assertSuggestions } = await setup('^'); + assertSuggestions('FROM index | EVAL /* ^', []); + }); + + test('suggests next to comments', async () => { + const { suggest } = await setup('^'); + expect((await suggest('FROM index | EVAL ^/* */')).length).toBeGreaterThan(0); + expect((await suggest('FROM index | EVAL /* */^')).length).toBeGreaterThan(0); + expect((await suggest('FROM index | EVAL ^// a comment')).length).toBeGreaterThan(0); + expect((await suggest('FROM index | EVAL // a comment\n^')).length).toBeGreaterThan(0); + }); + + test('handles multiple comments', async () => { + const { assertSuggestions } = await setup('^'); + assertSuggestions('FROM index | EVAL /* comment1 */ x + /* comment2 ^ */ 1', []); + assertSuggestions('FROM index | EVAL /* ^ comment1 */ x + /* comment2 ^ */ 1', []); + assertSuggestions('FROM index | EVAL /* comment1 */ x + /* comment2 */ 1 // comment3 ^', []); + }); +}); diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts index b89be15d670b1..9e7fa4566d753 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts @@ -12,7 +12,6 @@ import { scalarFunctionDefinitions } from '../definitions/generated/scalar_funct import { timeUnitsToSuggest } from '../definitions/literals'; import { commandDefinitions as unmodifiedCommandDefinitions } from '../definitions/commands'; import { - getAddDateHistogramSnippet, getDateLiterals, getSafeInsertText, TIME_SYSTEM_PARAMS, @@ -38,6 +37,7 @@ import { METADATA_FIELDS } from '../shared/constants'; import { ESQL_COMMON_NUMERIC_TYPES, ESQL_STRING_TYPES } from '../shared/esql_types'; import { log10ParameterTypes, powParameterTypes } from './__tests__/constants'; import { getRecommendedQueries } from './recommended_queries/templates'; +import { getDateHistogramCompletionItem } from './commands/stats/util'; const commandDefinitions = unmodifiedCommandDefinitions.filter(({ hidden }) => !hidden); @@ -104,7 +104,7 @@ describe('autocomplete', () => { .map(({ name }) => name.toUpperCase() + ' $0') ); testSuggestions( - 'from a [metadata _id] | /', + 'from a metadata _id | /', commandDefinitions .filter(({ name }) => !sourceCommands.includes(name)) .map(({ name }) => name.toUpperCase() + ' $0') @@ -116,7 +116,7 @@ describe('autocomplete', () => { .map(({ name }) => name.toUpperCase() + ' $0') ); testSuggestions( - 'from a [metadata _id] | eval var0 = a | /', + 'from a metadata _id | eval var0 = a | /', commandDefinitions .filter(({ name }) => !sourceCommands.includes(name)) .map(({ name }) => name.toUpperCase() + ' $0') @@ -737,12 +737,12 @@ describe('autocomplete', () => { ]); // STATS argument BY - testSuggestions('FROM index1 | STATS AVG(booleanField) B/', ['BY $0', ',', '| ']); + testSuggestions('FROM index1 | STATS AVG(booleanField) B/', ['BY ', ', ', '| ']); // STATS argument BY expression testSuggestions('FROM index1 | STATS field BY f/', [ 'var0 = ', - getAddDateHistogramSnippet(), + getDateHistogramCompletionItem(), ...getFunctionSignaturesByReturnType('stats', 'any', { grouping: true, scalar: true }), ...getFieldNamesByType('any').map((field) => `${field} `), ]); @@ -1072,8 +1072,8 @@ describe('autocomplete', () => { // STATS argument BY testSuggestions('FROM a | STATS AVG(numberField) /', [ - ',', - attachAsSnippet(attachTriggerCommand('BY $0')), + ', ', + attachTriggerCommand('BY '), attachTriggerCommand('| '), ]); @@ -1090,7 +1090,7 @@ describe('autocomplete', () => { 'by' ); testSuggestions('FROM a | STATS AVG(numberField) BY /', [ - getAddDateHistogramSnippet(), + getDateHistogramCompletionItem(), attachTriggerCommand('var0 = '), ...getFieldNamesByType('any') .map((field) => `${field} `) @@ -1100,7 +1100,7 @@ describe('autocomplete', () => { // STATS argument BY assignment (checking field suggestions) testSuggestions('FROM a | STATS AVG(numberField) BY var0 = /', [ - getAddDateHistogramSnippet(), + getDateHistogramCompletionItem(), ...getFieldNamesByType('any') .map((field) => `${field} `) .map(attachTriggerCommand), diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.ts index 5bdbd9d995fc9..ecb46682b041e 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.ts @@ -16,7 +16,6 @@ import type { ESQLFunction, ESQLSingleAstItem, } from '@kbn/esql-ast'; -import { i18n } from '@kbn/i18n'; import { ESQL_NUMBER_TYPES, isNumericType } from '../shared/esql_types'; import type { EditorContext, ItemKind, SuggestionRawDefinition, GetColumnsByTypeFn } from './types'; import { @@ -24,7 +23,7 @@ import { getCommandDefinition, getCommandOption, getFunctionDefinition, - getLastCharFromTrimmed, + getLastNonWhitespaceChar, isArrayType, isAssignment, isAssignmentComplete, @@ -68,9 +67,9 @@ import { buildFieldsDefinitions, buildPoliciesDefinitions, buildSourcesDefinitions, - buildNewVarDefinition, + getNewVariableSuggestion, buildNoPoliciesAvailableDefinition, - getCompatibleFunctionDefinition, + getFunctionSuggestions, buildMatchingFieldsDefinition, getCompatibleLiterals, buildConstantsDefinitions, @@ -81,7 +80,6 @@ import { getDateLiterals, buildFieldsDefinitionsWithMetadata, TRIGGER_SUGGESTION_COMMAND, - getAddDateHistogramSnippet, } from './factories'; import { EDITOR_MARKER, METADATA_FIELDS } from '../shared/constants'; import { getAstContext, removeMarkerArgFromArgsList } from '../shared/context'; @@ -109,7 +107,6 @@ import { import { FunctionParameter, isParameterType, isReturnType } from '../definitions/types'; import { metadataOption } from '../definitions/options'; import { comparisonFunctions } from '../definitions/builtin'; -import { countBracketsUnclosed } from '../shared/helpers'; import { getRecommendedQueriesSuggestions } from './recommended_queries/suggestions'; type GetFieldsMapFn = () => Promise>; @@ -162,6 +159,11 @@ export async function suggest( const { ast } = await astProvider(correctedQuery); const astContext = getAstContext(innerText, ast, offset); + + if (astContext.type === 'comment') { + return []; + } + // build the correct query to fetch the list of fields const queryForFields = getQueryForFields( buildQueryUntilPreviousCommand(ast, correctedQuery), @@ -206,9 +208,7 @@ export async function suggest( } if (astContext.type === 'expression') { - // suggest next possible argument, or option - // otherwise a variable - return getSuggestionsWithinCommand( + return getSuggestionsWithinCommandExpression( innerText, ast, astContext, @@ -216,7 +216,8 @@ export async function suggest( getFieldsByType, getFieldsMap, getPolicies, - getPolicyMetadata + getPolicyMetadata, + resourceRetriever?.getPreferences ); } if (astContext.type === 'setting') { @@ -239,8 +240,7 @@ export async function suggest( { option, ...rest }, getFieldsByType, getFieldsMap, - getPolicyMetadata, - resourceRetriever?.getPreferences + getPolicyMetadata ); } } @@ -444,7 +444,7 @@ function extractArgMeta( return { argIndex, prevIndex, lastArg, nodeArg }; } -async function getSuggestionsWithinCommand( +async function getSuggestionsWithinCommandExpression( innerText: string, commands: ESQLCommand[], { @@ -460,7 +460,8 @@ async function getSuggestionsWithinCommand( getColumnsByType: GetColumnsByTypeFn, getFieldsMap: GetFieldsMapFn, getPolicies: GetPoliciesFn, - getPolicyMetadata: GetPolicyMetadataFn + getPolicyMetadata: GetPolicyMetadataFn, + getPreferences?: () => Promise<{ histogramBarTarget: number } | undefined> ) { const commandDef = getCommandDefinition(command.name); @@ -471,8 +472,13 @@ async function getSuggestionsWithinCommand( const references = { fields: fieldsMap, variables: anyVariables }; if (commandDef.suggest) { // The new path. - return commandDef.suggest(innerText, command, getColumnsByType, (col: string) => - Boolean(getColumnByName(col, references)) + return commandDef.suggest( + innerText, + command, + getColumnsByType, + (col: string) => Boolean(getColumnByName(col, references)), + () => findNewVariable(anyVariables), + getPreferences ); } else { // The deprecated path. @@ -631,7 +637,7 @@ async function getExpressionSuggestionsByType( // ... | STATS ..., // ... | EVAL // ... | EVAL ..., - suggestions.push(buildNewVarDefinition(findNewVariable(anyVariables))); + suggestions.push(getNewVariableSuggestion(findNewVariable(anyVariables))); } } } @@ -1348,12 +1354,14 @@ async function getFunctionArgsSuggestions( // Functions suggestions.push( - ...getCompatibleFunctionDefinition( - command.name, - option?.name, - canBeBooleanCondition ? ['any'] : (getTypesFromParamDefs(typesToSuggestNext) as string[]), - fnToIgnore - ).map((suggestion) => ({ + ...getFunctionSuggestions({ + command: command.name, + option: option?.name, + returnTypes: canBeBooleanCondition + ? ['any'] + : (getTypesFromParamDefs(typesToSuggestNext) as string[]), + ignored: fnToIgnore, + }).map((suggestion) => ({ ...suggestion, text: addCommaIf(shouldAddComma, suggestion.text), })) @@ -1485,7 +1493,7 @@ async function getSettingArgsSuggestions( const settingDefs = getCommandDefinition(command.name).modes || []; if (settingDefs.length) { - const lastChar = getLastCharFromTrimmed(innerText); + const lastChar = getLastNonWhitespaceChar(innerText); const matchingSettingDefs = settingDefs.filter(({ prefix }) => lastChar === prefix); if (matchingSettingDefs.length) { // COMMAND _ @@ -1495,6 +1503,10 @@ async function getSettingArgsSuggestions( return suggestions; } +/** + * @deprecated — this will disappear when https://github.com/elastic/kibana/issues/195418 is complete + * because "options" will be handled in imperative command-specific routines instead of being independent. + */ async function getOptionArgsSuggestions( innerText: string, commands: ESQLCommand[], @@ -1509,29 +1521,19 @@ async function getOptionArgsSuggestions( }, getFieldsByType: GetColumnsByTypeFn, getFieldsMaps: GetFieldsMapFn, - getPolicyMetadata: GetPolicyMetadataFn, - getPreferences?: () => Promise<{ histogramBarTarget: number } | undefined> + getPolicyMetadata: GetPolicyMetadataFn ) { - let preferences: { histogramBarTarget: number } | undefined; - if (getPreferences) { - preferences = await getPreferences(); - } - const optionDef = getCommandOption(option.name); if (!optionDef || !optionDef.signature) { return []; } - const { nodeArg, argIndex, lastArg } = extractArgMeta(option, node); + const { nodeArg, lastArg } = extractArgMeta(option, node); const suggestions = []; const isNewExpression = isRestartingExpression(innerText) || option.args.length === 0; const fieldsMap = await getFieldsMaps(); const anyVariables = collectVariables(commands, fieldsMap, innerText); - const references = { - fields: fieldsMap, - variables: anyVariables, - }; if (command.name === 'enrich') { if (option.name === 'on') { // if it's a new expression, suggest fields to match on @@ -1573,7 +1575,7 @@ async function getOptionArgsSuggestions( ); if (isNewExpression || noCaseCompare(findPreviousWord(innerText), 'WITH')) { - suggestions.push(buildNewVarDefinition(findNewVariable(anyEnhancedVariables))); + suggestions.push(getNewVariableSuggestion(findNewVariable(anyEnhancedVariables))); } // make sure to remove the marker arg from the assign fn @@ -1696,53 +1698,6 @@ async function getOptionArgsSuggestions( } } - if (command.name === 'stats') { - const argDef = optionDef?.signature.params[argIndex]; - - const nodeArgType = extractTypeFromASTArg(nodeArg, references); - // These cases can happen here, so need to identify each and provide the right suggestion - // i.e. ... | STATS ... BY field + - // i.e. ... | STATS ... BY field >= - - if (nodeArgType) { - if (isFunctionItem(nodeArg) && !isFunctionArgComplete(nodeArg, references).complete) { - suggestions.push( - ...(await getBuiltinFunctionNextArgument( - innerText, - command, - option, - { type: argDef?.type || 'unknown' }, - nodeArg, - nodeArgType as string, - { - fields: references.fields, - // you can't use a variable defined - // in the stats command in the by clause - variables: new Map(), - }, - getFieldsByType - )) - ); - } - } - - // If it's a complete expression then propose some final suggestions - if ( - (!nodeArgType && - option.name === 'by' && - option.args.length && - !isNewExpression && - !isAssignment(lastArg)) || - (isAssignment(lastArg) && isAssignmentComplete(lastArg)) - ) { - suggestions.push( - ...getFinalSuggestions({ - comma: optionDef?.signature.multipleParams ?? option.name === 'by', - }) - ); - } - } - if (optionDef) { if (!suggestions.length) { const argDefIndex = optionDef.signature.multipleParams @@ -1768,52 +1723,6 @@ async function getOptionArgsSuggestions( openSuggestions: true, })) ); - // Checks if cursor is still within function () - // by checking if the marker editor/cursor is within an unclosed parenthesis - const canHaveAssignment = countBracketsUnclosed('(', innerText) === 0; - - if (option.name === 'by') { - // Add quick snippet for for stats ... by bucket(<>) - if (command.name === 'stats' && canHaveAssignment) { - suggestions.push({ - label: i18n.translate( - 'kbn-esql-validation-autocomplete.esql.autocomplete.addDateHistogram', - { - defaultMessage: 'Add date histogram', - } - ), - text: getAddDateHistogramSnippet(preferences?.histogramBarTarget), - asSnippet: true, - kind: 'Issue', - detail: i18n.translate( - 'kbn-esql-validation-autocomplete.esql.autocomplete.addDateHistogramDetail', - { - defaultMessage: 'Add date histogram using bucket()', - } - ), - sortText: '1A', - command: TRIGGER_SUGGESTION_COMMAND, - } as SuggestionRawDefinition); - } - - suggestions.push( - ...(await getFieldsOrFunctionsSuggestions( - types[0] === 'column' ? ['any'] : types, - command.name, - option.name, - getFieldsByType, - { - functions: true, - fields: false, - }, - { ignoreFn: canHaveAssignment ? [] : ['bucket', 'case'] } - )) - ); - } - - if (command.name === 'stats' && isNewExpression && canHaveAssignment) { - suggestions.push(buildNewVarDefinition(findNewVariable(anyVariables))); - } } } } diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/commands/drop/index.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/commands/drop/index.ts index ed5f0ee3d3f6b..43eb272ba203a 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/commands/drop/index.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/commands/drop/index.ts @@ -10,7 +10,7 @@ import type { ESQLCommand } from '@kbn/esql-ast'; import { findPreviousWord, - getLastCharFromTrimmed, + getLastNonWhitespaceChar, isColumnItem, noCaseCompare, } from '../../../shared/helpers'; @@ -27,7 +27,7 @@ export async function suggest( ): Promise { if ( /\s/.test(innerText[innerText.length - 1]) && - getLastCharFromTrimmed(innerText) !== ',' && + getLastNonWhitespaceChar(innerText) !== ',' && !noCaseCompare(findPreviousWord(innerText), 'drop') ) { return [pipeCompleteItem, commaCompleteItem]; diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/commands/keep/index.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/commands/keep/index.ts index c2480ffbcde72..85d5c716b20a6 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/commands/keep/index.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/commands/keep/index.ts @@ -10,7 +10,7 @@ import type { ESQLCommand } from '@kbn/esql-ast'; import { findPreviousWord, - getLastCharFromTrimmed, + getLastNonWhitespaceChar, isColumnItem, noCaseCompare, } from '../../../shared/helpers'; @@ -27,7 +27,7 @@ export async function suggest( ): Promise { if ( /\s/.test(innerText[innerText.length - 1]) && - getLastCharFromTrimmed(innerText) !== ',' && + getLastNonWhitespaceChar(innerText) !== ',' && !noCaseCompare(findPreviousWord(innerText), 'keep') ) { return [pipeCompleteItem, commaCompleteItem]; diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/commands/stats/index.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/commands/stats/index.ts new file mode 100644 index 0000000000000..46a37d36eacc9 --- /dev/null +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/commands/stats/index.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { ESQLCommand } from '@kbn/esql-ast'; +import type { GetColumnsByTypeFn, SuggestionRawDefinition } from '../../types'; +import { + TRIGGER_SUGGESTION_COMMAND, + getNewVariableSuggestion, + getFunctionSuggestions, +} from '../../factories'; +import { commaCompleteItem, pipeCompleteItem } from '../../complete_items'; +import { pushItUpInTheList } from '../../helper'; +import { byCompleteItem, getDateHistogramCompletionItem, getPosition } from './util'; + +export async function suggest( + innerText: string, + command: ESQLCommand<'stats'>, + getColumnsByType: GetColumnsByTypeFn, + _columnExists: (column: string) => boolean, + getSuggestedVariableName: () => string, + getPreferences?: () => Promise<{ histogramBarTarget: number } | undefined> +): Promise { + const pos = getPosition(innerText, command); + + const columnSuggestions = pushItUpInTheList( + await getColumnsByType('any', [], { advanceCursor: true, openSuggestions: true }), + true + ); + + switch (pos) { + case 'expression_without_assignment': + return [ + ...getFunctionSuggestions({ command: 'stats' }), + getNewVariableSuggestion(getSuggestedVariableName()), + ]; + + case 'expression_after_assignment': + return [...getFunctionSuggestions({ command: 'stats' })]; + + case 'expression_complete': + return [ + byCompleteItem, + pipeCompleteItem, + { ...commaCompleteItem, command: TRIGGER_SUGGESTION_COMMAND, text: ', ' }, + ]; + + case 'grouping_expression_after_assignment': + return [ + ...getFunctionSuggestions({ command: 'stats', option: 'by' }), + getDateHistogramCompletionItem((await getPreferences?.())?.histogramBarTarget), + ...columnSuggestions, + ]; + + case 'grouping_expression_without_assignment': + return [ + ...getFunctionSuggestions({ command: 'stats', option: 'by' }), + getDateHistogramCompletionItem((await getPreferences?.())?.histogramBarTarget), + ...columnSuggestions, + getNewVariableSuggestion(getSuggestedVariableName()), + ]; + + case 'grouping_expression_complete': + return [ + pipeCompleteItem, + { ...commaCompleteItem, command: TRIGGER_SUGGESTION_COMMAND, text: ', ' }, + ]; + + default: + return []; + } +} diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/commands/stats/util.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/commands/stats/util.ts new file mode 100644 index 0000000000000..c9abaa5c5408a --- /dev/null +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/commands/stats/util.ts @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { ESQLCommand } from '@kbn/esql-ast'; +import { i18n } from '@kbn/i18n'; +import { + findPreviousWord, + getLastNonWhitespaceChar, + isAssignment, + isAssignmentComplete, + isOptionItem, + noCaseCompare, +} from '../../../shared/helpers'; +import { SuggestionRawDefinition } from '../../types'; +import { TIME_SYSTEM_PARAMS, TRIGGER_SUGGESTION_COMMAND } from '../../factories'; + +/** + * Position of the caret in the sort command: +* +* ``` +* STATS [column1 =] expression1[, ..., [columnN =] expressionN] [BY [column1 =] grouping_expression1[, ..., grouping_expressionN]] + | | | | | | + | | expression_complete | | grouping_expression_complete + | expression_after_assignment | grouping_expression_after_assignment + expression_without_assignment grouping_expression_without_assignment + +* ``` +*/ +export type CaretPosition = + | 'expression_without_assignment' + | 'expression_after_assignment' + | 'expression_complete' + | 'grouping_expression_without_assignment' + | 'grouping_expression_after_assignment' + | 'grouping_expression_complete'; + +export const getPosition = (innerText: string, command: ESQLCommand): CaretPosition => { + const lastCommandArg = command.args[command.args.length - 1]; + + if (isOptionItem(lastCommandArg) && lastCommandArg.name === 'by') { + // in the BY clause + + const lastOptionArg = lastCommandArg.args[lastCommandArg.args.length - 1]; + if (isAssignment(lastOptionArg) && !isAssignmentComplete(lastOptionArg)) { + return 'grouping_expression_after_assignment'; + } + + if ( + getLastNonWhitespaceChar(innerText) === ',' || + noCaseCompare(findPreviousWord(innerText), 'by') + ) { + return 'grouping_expression_without_assignment'; + } else { + return 'grouping_expression_complete'; + } + } + + if (isAssignment(lastCommandArg) && !isAssignmentComplete(lastCommandArg)) { + return 'expression_after_assignment'; + } + + if ( + getLastNonWhitespaceChar(innerText) === ',' || + noCaseCompare(findPreviousWord(innerText), 'stats') + ) { + return 'expression_without_assignment'; + } else { + return 'expression_complete'; + } +}; + +export const byCompleteItem: SuggestionRawDefinition = { + label: 'BY', + text: 'BY ', + kind: 'Reference', + detail: 'By', + sortText: '1', + command: TRIGGER_SUGGESTION_COMMAND, +}; + +export const getDateHistogramCompletionItem: ( + histogramBarTarget?: number +) => SuggestionRawDefinition = (histogramBarTarget: number = 50) => ({ + label: i18n.translate('kbn-esql-validation-autocomplete.esql.autocomplete.addDateHistogram', { + defaultMessage: 'Add date histogram', + }), + text: `BUCKET($0, ${histogramBarTarget}, ${TIME_SYSTEM_PARAMS.join(', ')})`, + asSnippet: true, + kind: 'Issue', + detail: i18n.translate( + 'kbn-esql-validation-autocomplete.esql.autocomplete.addDateHistogramDetail', + { + defaultMessage: 'Add date histogram using bucket()', + } + ), + sortText: '1A', + command: TRIGGER_SUGGESTION_COMMAND, +}); diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts index f522e9bc65863..9b7e2b0bf71a5 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts @@ -39,10 +39,6 @@ const allFunctions = memoize( export const TIME_SYSTEM_PARAMS = ['?_tstart', '?_tend']; -export const getAddDateHistogramSnippet = (histogramBarTarget = 50) => { - return `BUCKET($0, ${histogramBarTarget}, ${TIME_SYSTEM_PARAMS.join(', ')})`; -}; - export const TRIGGER_SUGGESTION_COMMAND = { title: 'Trigger Suggestion Dialog', id: 'editor.action.triggerSuggest', @@ -61,7 +57,7 @@ function getSafeInsertSourceText(text: string) { return shouldBeQuotedSource(text) ? getQuotedText(text) : text; } -export function getSuggestionFunctionDefinition(fn: FunctionDefinition): SuggestionRawDefinition { +export function getFunctionSuggestion(fn: FunctionDefinition): SuggestionRawDefinition { const fullSignatures = getFunctionSignatures(fn, { capitalize: true, withTypes: true }); return { label: fn.name.toUpperCase(), @@ -95,31 +91,47 @@ export function getSuggestionBuiltinDefinition(fn: FunctionDefinition): Suggesti }; } -export const getCompatibleFunctionDefinition = ( - command: string, - option: string | undefined, - returnTypes?: string[], - ignored: string[] = [] -): SuggestionRawDefinition[] => { - const fnSupportedByCommand = allFunctions() - .filter( - ({ name, supportedCommands, supportedOptions, ignoreAsSuggestion }) => - (option ? supportedOptions?.includes(option) : supportedCommands.includes(command)) && - !ignored.includes(name) && - !ignoreAsSuggestion - ) - .sort((a, b) => a.name.localeCompare(b.name)); - if (!returnTypes) { - return fnSupportedByCommand.map(getSuggestionFunctionDefinition); - } - return fnSupportedByCommand - .filter((mathDefinition) => - mathDefinition.signatures.some( - (signature) => - returnTypes[0] === 'any' || returnTypes.includes(signature.returnType as string) - ) - ) - .map(getSuggestionFunctionDefinition); +/** + * Builds suggestions for functions based on the provided predicates. + * + * @param predicates a set of conditions that must be met for a function to be included in the suggestions + * @returns + */ +export const getFunctionSuggestions = (predicates?: { + command?: string; + option?: string | undefined; + returnTypes?: string[]; + ignored?: string[]; +}): SuggestionRawDefinition[] => { + const functions = allFunctions(); + const { command, option, returnTypes, ignored = [] } = predicates ?? {}; + const filteredFunctions: FunctionDefinition[] = functions.filter( + ({ name, supportedCommands, supportedOptions, ignoreAsSuggestion, signatures }) => { + if (ignoreAsSuggestion) { + return false; + } + + if (ignored.includes(name)) { + return false; + } + + if (option && !supportedOptions?.includes(option)) { + return false; + } + + if (command && !supportedCommands.includes(command)) { + return false; + } + + if (returnTypes && !returnTypes.includes('any')) { + return signatures.some((signature) => returnTypes.includes(signature.returnType as string)); + } + + return true; + } + ); + + return filteredFunctions.map(getFunctionSuggestion); }; export function getSuggestionCommandDefinition( @@ -253,7 +265,7 @@ export const buildValueDefinitions = ( command: options?.advanceCursorAndOpenSuggestions ? TRIGGER_SUGGESTION_COMMAND : undefined, })); -export const buildNewVarDefinition = (label: string): SuggestionRawDefinition => { +export const getNewVariableSuggestion = (label: string): SuggestionRawDefinition => { return { label, text: `${label} = `, diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/helper.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/helper.ts index 6585a04c98c59..9724878611b01 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/helper.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/helper.ts @@ -35,7 +35,7 @@ import { compareTypesWithLiterals } from '../shared/esql_types'; import { TIME_SYSTEM_PARAMS, buildVariablesDefinitions, - getCompatibleFunctionDefinition, + getFunctionSuggestions, getCompatibleLiterals, getDateLiterals, } from './factories'; @@ -417,7 +417,14 @@ export async function getFieldsOrFunctionsSuggestions( const suggestions = filteredFieldsByType.concat( displayDateSuggestions ? getDateLiterals() : [], - functions ? getCompatibleFunctionDefinition(commandName, optionName, types, ignoreFn) : [], + functions + ? getFunctionSuggestions({ + command: commandName, + option: optionName, + returnTypes: types, + ignored: ignoreFn, + }) + : [], variables ? pushItUpInTheList(buildVariablesDefinitions(filteredVariablesByType), functions) : [], diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/commands.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/commands.ts index f4482a5b33c17..d07511665ad31 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/commands.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/commands.ts @@ -35,6 +35,7 @@ import type { CommandDefinition } from './types'; import { suggest as suggestForSort } from '../autocomplete/commands/sort'; import { suggest as suggestForKeep } from '../autocomplete/commands/keep'; import { suggest as suggestForDrop } from '../autocomplete/commands/drop'; +import { suggest as suggestForStats } from '../autocomplete/commands/stats'; const statsValidator = (command: ESQLCommand) => { const messages: ESQLMessage[] = []; @@ -240,6 +241,7 @@ export const commandDefinitions: Array> = [ options: [byOption], modes: [], validate: statsValidator, + suggest: suggestForStats, }, { name: 'inlinestats', diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/options.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/options.ts index 31d443a8cbb2b..87c91ddaabaa0 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/options.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/options.ts @@ -38,16 +38,6 @@ export const metadataOption: CommandOptionsDefinition = { skipCommonValidation: true, validate: (option, command, references) => { const messages: ESQLMessage[] = []; - // need to test the parent command here - if (/\[metadata/i.test(command.text)) { - messages.push( - getMessageFromId({ - messageId: 'metadataBracketsDeprecation', - values: {}, - locations: option.location, - }) - ); - } const fields = option.args.filter(isColumnItem); const metadataFieldsAvailable = references as unknown as Set; if (metadataFieldsAvailable.size > 0) { diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/types.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/types.ts index a83908b41617f..ff461683d8e76 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/types.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/types.ts @@ -171,7 +171,9 @@ export interface CommandBaseDefinition { innerText: string, command: ESQLCommand, getColumnsByType: GetColumnsByTypeFn, - columnExists: (column: string) => boolean + columnExists: (column: string) => boolean, + getSuggestedVariableName: () => string, + getPreferences?: () => Promise<{ histogramBarTarget: number } | undefined> ) => Promise; /** @deprecated this property will disappear in the future */ signature: { diff --git a/packages/kbn-esql-validation-autocomplete/src/shared/context.ts b/packages/kbn-esql-validation-autocomplete/src/shared/context.ts index 1c2e9075e95ff..42e63d7623e49 100644 --- a/packages/kbn-esql-validation-autocomplete/src/shared/context.ts +++ b/packages/kbn-esql-validation-autocomplete/src/shared/context.ts @@ -7,24 +7,25 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import type { - ESQLAstItem, - ESQLSingleAstItem, - ESQLAst, - ESQLFunction, - ESQLCommand, - ESQLCommandOption, - ESQLCommandMode, +import { + type ESQLAstItem, + type ESQLSingleAstItem, + type ESQLAst, + type ESQLFunction, + type ESQLCommand, + type ESQLCommandOption, + type ESQLCommandMode, + Walker, } from '@kbn/esql-ast'; import { ENRICH_MODES } from '../definitions/settings'; import { EDITOR_MARKER } from './constants'; import { isOptionItem, isColumnItem, - getFunctionDefinition, isSourceItem, isSettingItem, pipePrecedesCurrentWord, + getFunctionDefinition, } from './helpers'; function findNode(nodes: ESQLAstItem[], offset: number): ESQLSingleAstItem | undefined { @@ -133,6 +134,7 @@ function findAstPosition(ast: ESQLAst, offset: number) { function isNotEnrichClauseAssigment(node: ESQLFunction, command: ESQLCommand) { return node.name !== '=' && command.name !== 'enrich'; } + function isBuiltinFunction(node: ESQLFunction) { return getFunctionDefinition(node.name)?.type === 'builtin'; } @@ -151,6 +153,20 @@ function isBuiltinFunction(node: ESQLFunction) { * * "newCommand": the cursor is at the beginning of a new command (i.e. `command1 | command2 | `) */ export function getAstContext(queryString: string, ast: ESQLAst, offset: number) { + let inComment = false; + + Walker.visitComments(ast, (node) => { + if (node.location && node.location.min <= offset && node.location.max > offset) { + inComment = true; + } + }); + + if (inComment) { + return { + type: 'comment' as const, + }; + } + const { command, option, setting, node } = findAstPosition(ast, offset); if (node) { if (node.type === 'literal' && node.literalType === 'keyword') { @@ -162,15 +178,18 @@ export function getAstContext(queryString: string, ast: ESQLAst, offset: number) // command ... a in ( ) return { type: 'list' as const, command, node, option, setting }; } - if (isNotEnrichClauseAssigment(node, command) && !isBuiltinFunction(node)) { + if ( + isNotEnrichClauseAssigment(node, command) && + // Temporarily mangling the logic here to let operators + // be handled as functions for the stats command. + // I expect this to simplify once https://github.com/elastic/kibana/issues/195418 + // is complete + !(isBuiltinFunction(node) && command.name !== 'stats') + ) { // command ... fn( ) return { type: 'function' as const, command, node, option, setting }; } } - if (node.type === 'option' || option) { - // command ... by - return { type: 'option' as const, command, node, option, setting }; - } // for now it's only an enrich thing if (node.type === 'source' && node.text === ENRICH_MODES.prefix) { // command _ @@ -182,7 +201,8 @@ export function getAstContext(queryString: string, ast: ESQLAst, offset: number) return { type: 'newCommand' as const, command: undefined, node, option, setting }; } - if (command && isOptionItem(command.args[command.args.length - 1])) { + // TODO — remove this option branch once https://github.com/elastic/kibana/issues/195418 is complete + if (command && isOptionItem(command.args[command.args.length - 1]) && command.name !== 'stats') { if (option) { return { type: 'option' as const, command, node, option, setting }; } diff --git a/packages/kbn-esql-validation-autocomplete/src/shared/helpers.test.ts b/packages/kbn-esql-validation-autocomplete/src/shared/helpers.test.ts index b5f14ecfd0227..97f35e1c66722 100644 --- a/packages/kbn-esql-validation-autocomplete/src/shared/helpers.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/shared/helpers.test.ts @@ -8,8 +8,8 @@ */ import { parse } from '@kbn/esql-ast'; -import { getExpressionType, shouldBeQuotedSource } from './helpers'; -import type { SupportedDataType } from '../definitions/types'; +import { getBracketsToClose, getExpressionType, shouldBeQuotedSource } from './helpers'; +import { SupportedDataType } from '../definitions/types'; import { setTestFunctions } from './test_functions'; describe('shouldBeQuotedSource', () => { @@ -324,3 +324,16 @@ describe('getExpressionType', () => { ); }); }); + +describe('getBracketsToClose', () => { + it('returns the number of brackets to close', () => { + expect(getBracketsToClose('foo(bar(baz')).toEqual([')', ')']); + expect(getBracketsToClose('foo(bar[baz')).toEqual([']', ')']); + expect(getBracketsToClose('foo(bar[baz"bap')).toEqual(['"', ']', ')']); + expect( + getBracketsToClose( + 'from a | eval case(integerField < 0, "negative", integerField > 0, "positive", ' + ) + ).toEqual([')']); + }); +}); diff --git a/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts index 02dff9720cd9b..43bbb2b571a50 100644 --- a/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts @@ -599,7 +599,7 @@ export function pipePrecedesCurrentWord(text: string) { return characterPrecedesCurrentWord(text, '|'); } -export function getLastCharFromTrimmed(text: string) { +export function getLastNonWhitespaceChar(text: string) { return text[text.trimEnd().length - 1]; } @@ -607,7 +607,7 @@ export function getLastCharFromTrimmed(text: string) { * Are we after a comma? i.e. STATS fieldA, */ export function isRestartingExpression(text: string) { - return getLastCharFromTrimmed(text) === ',' || characterPrecedesCurrentWord(text, ','); + return getLastNonWhitespaceChar(text) === ',' || characterPrecedesCurrentWord(text, ','); } export function findPreviousWord(text: string) { @@ -651,26 +651,59 @@ export const isParam = (x: unknown): x is ESQLParamLiteral => export const noCaseCompare = (a: string, b: string) => a.toLowerCase() === b.toLowerCase(); /** - * This function count the number of unclosed brackets in order to - * locally fix the queryString to generate a valid AST + * This function returns a list of closing brackets that can be appended to + * a partial query to make it valid. + +* locally fix the queryString to generate a valid AST * A known limitation of this is that is not aware of commas "," or pipes "|" * so it is not yet helpful on a multiple commands errors (a workaround it to pass each command here...) - * @param bracketType * @param text * @returns */ -export function countBracketsUnclosed(bracketType: '(' | '[' | '"' | '"""', text: string) { +export function getBracketsToClose(text: string) { const stack = []; - const closingBrackets = { '(': ')', '[': ']', '"': '"', '"""': '"""' }; + const pairs: Record = { '"""': '"""', '/*': '*/', '(': ')', '[': ']', '"': '"' }; + const pairsReversed: Record = { + '"""': '"""', + '*/': '/*', + ')': '(', + ']': '[', + '"': '"', + }; + for (let i = 0; i < text.length; i++) { - const substr = text.substring(i, i + bracketType.length); - if (substr === closingBrackets[bracketType] && stack.length) { - stack.pop(); - } else if (substr === bracketType) { - stack.push(bracketType); + for (const openBracket in pairs) { + if (!Object.hasOwn(pairs, openBracket)) { + continue; + } + + const substr = text.slice(i, i + openBracket.length); + if (substr === openBracket) { + stack.push(substr); + break; + } else if (pairsReversed[substr] && pairsReversed[substr] === stack[stack.length - 1]) { + stack.pop(); + break; + } } } - return stack.length; + return stack.reverse().map((bracket) => pairs[bracket]); +} + +/** + * This function counts the number of unclosed parentheses + * @param text + */ +export function countUnclosedParens(text: string) { + let unclosedCount = 0; + for (let i = 0; i < text.length; i++) { + if (text[i] === ')' && unclosedCount > 0) { + unclosedCount--; + } else if (text[i] === '(') { + unclosedCount++; + } + } + return unclosedCount; } /** @@ -685,37 +718,22 @@ export function countBracketsUnclosed(bracketType: '(' | '[' | '"' | '"""', text export function correctQuerySyntax(_query: string, context: EditorContext) { let query = _query; // check if all brackets are closed, otherwise close them - const unclosedRoundBrackets = countBracketsUnclosed('(', query); - const unclosedSquaredBrackets = countBracketsUnclosed('[', query); - const unclosedQuotes = countBracketsUnclosed('"', query); - const unclosedTripleQuotes = countBracketsUnclosed('"""', query); + const bracketsToAppend = getBracketsToClose(query); + const unclosedRoundBracketCount = bracketsToAppend.filter((bracket) => bracket === ')').length; // if it's a comma by the user or a forced trigger by a function argument suggestion // add a marker to make the expression still valid const charThatNeedMarkers = [',', ':']; if ( (context.triggerCharacter && charThatNeedMarkers.includes(context.triggerCharacter)) || // monaco.editor.CompletionTriggerKind['Invoke'] === 0 - (context.triggerKind === 0 && unclosedRoundBrackets === 0) || + (context.triggerKind === 0 && unclosedRoundBracketCount === 0) || (context.triggerCharacter === ' ' && isMathFunction(query, query.length)) || isComma(query.trimEnd()[query.trimEnd().length - 1]) ) { query += EDITOR_MARKER; } - // if there are unclosed brackets, close them - if (unclosedRoundBrackets || unclosedSquaredBrackets || unclosedQuotes) { - for (const [char, count] of [ - ['"""', unclosedTripleQuotes], - ['"', unclosedQuotes], - [')', unclosedRoundBrackets], - [']', unclosedSquaredBrackets], - ]) { - if (count) { - // inject the closing brackets - query += Array(count).fill(char).join(''); - } - } - } + query += bracketsToAppend.join(''); return query; } diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.from.ts b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.from.ts index 491c44fe699df..2a316cdb7a2e9 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.from.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.from.ts @@ -106,7 +106,7 @@ export const validationFromCommandTestSuite = (setup: helpers.Setup) => { await expectErrors('from index metadata _id, \t\n _index\n ', []); }); - test('errors when wrapped in brackets', async () => { + test('errors when wrapped in parentheses', async () => { const { expectErrors } = await setup(); await expectErrors(`from index (metadata _id)`, [ @@ -114,65 +114,20 @@ export const validationFromCommandTestSuite = (setup: helpers.Setup) => { ]); }); - for (const isWrapped of [true, false]) { - function setWrapping(option: string) { - return isWrapped ? `[${option}]` : option; - } - - function addBracketsWarning() { - return isWrapped - ? ["Square brackets '[]' need to be removed from FROM METADATA declaration"] - : []; - } - - describe(`wrapped = ${isWrapped}`, () => { - test('no errors on correct usage, waning on square brackets', async () => { - const { expectErrors } = await setup(); - - await expectErrors(`from index ${setWrapping('METADATA _id')}`, []); - await expectErrors( - `from index ${setWrapping('METADATA _id')}`, - [], - addBracketsWarning() - ); - await expectErrors( - `from index ${setWrapping('metadata _id')}`, - [], - addBracketsWarning() - ); - await expectErrors( - `from index ${setWrapping('METADATA _id, _source')}`, - [], - addBracketsWarning() - ); - }); - - test('validates fields', async () => { - const { expectErrors } = await setup(); - - await expectErrors( - `from index ${setWrapping('METADATA _id, _source2')}`, - [ - `Metadata field [_source2] is not available. Available metadata fields are: [${METADATA_FIELDS.join( - ', ' - )}]`, - ], - addBracketsWarning() - ); - await expectErrors( - `from index ${setWrapping('metadata _id, _source')} ${setWrapping( - 'METADATA _id2' - )}`, - [ - isWrapped - ? "SyntaxError: mismatched input '[' expecting " - : "SyntaxError: mismatched input 'METADATA' expecting ", - ], - addBracketsWarning() - ); - }); + describe('validates fields', () => { + test('validates fields', async () => { + const { expectErrors } = await setup(); + + await expectErrors(`from index METADATA _id, _source2`, [ + `Metadata field [_source2] is not available. Available metadata fields are: [${METADATA_FIELDS.join( + ', ' + )}]`, + ]); + await expectErrors(`from index metadata _id, _source METADATA _id2`, [ + "SyntaxError: mismatched input 'METADATA' expecting ", + ]); }); - } + }); }); }); }); diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/validation.ccs.test.ts b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/validation.ccs.test.ts index 5cdb83be618d1..81e54b0b5cf1a 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/validation.ccs.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/validation.ccs.test.ts @@ -24,33 +24,13 @@ describe('validation', () => { }); describe('... METADATA ', () => { - for (const isWrapped of [true, false]) { - function setWrapping(option: string) { - return isWrapped ? `[${option}]` : option; - } - - function addBracketsWarning() { - return isWrapped - ? ["Square brackets '[]' need to be removed from FROM METADATA declaration"] - : []; - } - - describe(`wrapped = ${isWrapped}`, () => { - test('no errors on correct usage, waning on square brackets', async () => { - const { expectErrors } = await setup(); - await expectErrors( - `from remote-ccs:indexes ${setWrapping('METADATA _id')}`, - ['Unknown index [remote-ccs:indexes]'], - addBracketsWarning() - ); - await expectErrors( - `from *:indexes ${setWrapping('METADATA _id')}`, - ['Unknown index [*:indexes]'], - addBracketsWarning() - ); - }); - }); - } + test('no errors on correct usage', async () => { + const { expectErrors } = await setup(); + await expectErrors(`from remote-ccs:indexes METADATA _id`, [ + 'Unknown index [remote-ccs:indexes]', + ]); + await expectErrors(`from *:indexes METADATA _id`, ['Unknown index [*:indexes]']); + }); }); }); }); diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json b/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json index f1e71c9ff6a97..25fb880da5ff0 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json +++ b/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json @@ -9947,70 +9947,6 @@ ], "warning": [] }, - { - "query": "from index [METADATA _id]", - "error": [], - "warning": [] - }, - { - "query": "from index [METADATA _id]", - "error": [], - "warning": [ - "Square brackets '[]' need to be removed from FROM METADATA declaration" - ] - }, - { - "query": "from index [metadata _id]", - "error": [], - "warning": [ - "Square brackets '[]' need to be removed from FROM METADATA declaration" - ] - }, - { - "query": "from index [METADATA _id, _source]", - "error": [], - "warning": [ - "Square brackets '[]' need to be removed from FROM METADATA declaration" - ] - }, - { - "query": "from index [METADATA _id, _source2]", - "error": [ - "Metadata field [_source2] is not available. Available metadata fields are: [_version, _id, _index, _source, _ignored, _index_mode]" - ], - "warning": [ - "Square brackets '[]' need to be removed from FROM METADATA declaration" - ] - }, - { - "query": "from index [metadata _id, _source] [METADATA _id2]", - "error": [ - "SyntaxError: mismatched input '[' expecting " - ], - "warning": [ - "Square brackets '[]' need to be removed from FROM METADATA declaration" - ] - }, - { - "query": "from index METADATA _id", - "error": [], - "warning": [] - }, - { - "query": "from index METADATA _id", - "error": [], - "warning": [] - }, - { - "query": "from index metadata _id", - "error": [], - "warning": [] - }, - { - "query": "from index METADATA _id, _source", - "error": [], - "warning": [] - }, { "query": "from index METADATA _id, _source2", "error": [ @@ -10019,11 +9955,11 @@ "warning": [] }, { - "query": "from index metadata _id, _source METADATA _id2", + "query": "from index METADATA _id, _source METADATA _id2", "error": [ "SyntaxError: mismatched input 'METADATA' expecting " ], "warning": [] } ] -} \ No newline at end of file +} diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts b/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts index a9ecac9663609..e6546ec996547 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts @@ -1741,7 +1741,7 @@ describe('validation logic', () => { it('should basically work when all callbacks are passed', async () => { const allErrors = await Promise.all( fixtures.testCases - .filter(({ query }) => query === 'from index [METADATA _id, _source2]') + .filter(({ query }) => query === 'from index METADATA _id, _source2') .map(({ query }) => validateQuery( query, @@ -1753,7 +1753,7 @@ describe('validation logic', () => { ); for (const [index, { errors }] of Object.entries(allErrors)) { expect(errors.map((e) => ('severity' in e ? e.message : e.text))).toEqual( - fixtures.testCases.filter(({ query }) => query === 'from index [METADATA _id, _source2]')[ + fixtures.testCases.filter(({ query }) => query === 'from index METADATA _id, _source2')[ Number(index) ].error ); diff --git a/packages/kbn-ftr-common-functional-services/services/saml_auth/serverless/auth_provider.ts b/packages/kbn-ftr-common-functional-services/services/saml_auth/serverless/auth_provider.ts index 16c2dc9cfa844..b9583e3b8cb3a 100644 --- a/packages/kbn-ftr-common-functional-services/services/saml_auth/serverless/auth_provider.ts +++ b/packages/kbn-ftr-common-functional-services/services/saml_auth/serverless/auth_provider.ts @@ -7,6 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import getopts from 'getopts'; import { ServerlessProjectType, SERVERLESS_ROLES_ROOT_PATH } from '@kbn/es'; import { type Config } from '@kbn/test'; import { isServerlessProjectType, readRolesDescriptorsFromResource } from '@kbn/es/src/utils'; @@ -34,30 +35,23 @@ const getDefaultServerlessRole = (projectType: string) => { } }; -const isRoleManagementExplicitlyEnabled = (args: string[]): boolean => { - const roleManagementArg = args.find((arg) => - arg.startsWith('--xpack.security.roleManagementEnabled=') - ); - - // Return true if the value is explicitly set to 'true', otherwise false - return roleManagementArg?.split('=')[1] === 'true' || false; -}; - export class ServerlessAuthProvider implements AuthProvider { private readonly projectType: string; private readonly roleManagementEnabled: boolean; private readonly rolesDefinitionPath: string; constructor(config: Config) { - const kbnServerArgs = config.get('kbnTestServer.serverArgs') as string[]; - this.projectType = kbnServerArgs.reduce((acc, arg) => { - const match = arg.match(/--serverless[=\s](\w+)/); - return acc + (match ? match[1] : ''); - }, '') as ServerlessProjectType; + const options = getopts(config.get('kbnTestServer.serverArgs'), { + boolean: ['xpack.security.roleManagementEnabled'], + default: { + 'xpack.security.roleManagementEnabled': false, + }, + }); + this.projectType = options.serverless as ServerlessProjectType; // Indicates whether role management was explicitly enabled using // the `--xpack.security.roleManagementEnabled=true` flag. - this.roleManagementEnabled = isRoleManagementExplicitlyEnabled(kbnServerArgs); + this.roleManagementEnabled = options['xpack.security.roleManagementEnabled']; if (!isServerlessProjectType(this.projectType)) { throw new Error(`Unsupported serverless projectType: ${this.projectType}`); diff --git a/packages/kbn-grid-layout/grid/drag_preview.tsx b/packages/kbn-grid-layout/grid/drag_preview.tsx new file mode 100644 index 0000000000000..24aa81ffea1df --- /dev/null +++ b/packages/kbn-grid-layout/grid/drag_preview.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React, { useEffect, useRef } from 'react'; +import { combineLatest, skip } from 'rxjs'; + +import { transparentize } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; + +import { GridLayoutStateManager } from './types'; + +export const DragPreview = ({ + rowIndex, + gridLayoutStateManager, +}: { + rowIndex: number; + gridLayoutStateManager: GridLayoutStateManager; +}) => { + const dragPreviewRef = useRef(null); + + useEffect( + () => { + /** Update the styles of the drag preview via a subscription to prevent re-renders */ + const styleSubscription = combineLatest([ + gridLayoutStateManager.activePanel$, + gridLayoutStateManager.gridLayout$, + ]) + .pipe(skip(1)) // skip the first emit because the drag preview is only rendered after a user action + .subscribe(([activePanel, gridLayout]) => { + if (!dragPreviewRef.current) return; + + if (!activePanel || !gridLayout[rowIndex].panels[activePanel.id]) { + dragPreviewRef.current.style.display = 'none'; + } else { + const panel = gridLayout[rowIndex].panels[activePanel.id]; + dragPreviewRef.current.style.display = 'block'; + dragPreviewRef.current.style.gridColumnStart = `${panel.column + 1}`; + dragPreviewRef.current.style.gridColumnEnd = `${panel.column + 1 + panel.width}`; + dragPreviewRef.current.style.gridRowStart = `${panel.row + 1}`; + dragPreviewRef.current.style.gridRowEnd = `${panel.row + 1 + panel.height}`; + } + }); + + return () => { + styleSubscription.unsubscribe(); + }; + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + + return ( +
+ ); +}; diff --git a/packages/kbn-grid-layout/grid/grid_height_smoother.tsx b/packages/kbn-grid-layout/grid/grid_height_smoother.tsx index 7693fac72918a..960fe4f52e735 100644 --- a/packages/kbn-grid-layout/grid/grid_height_smoother.tsx +++ b/packages/kbn-grid-layout/grid/grid_height_smoother.tsx @@ -24,7 +24,7 @@ export const GridHeightSmoother = ({ gridLayoutStateManager.interactionEvent$, ]).subscribe(([dimensions, interactionEvent]) => { if (!smoothHeightRef.current) return; - if (!interactionEvent || interactionEvent.type === 'drop') { + if (!interactionEvent) { smoothHeightRef.current.style.height = `${dimensions.height}px`; return; } diff --git a/packages/kbn-grid-layout/grid/grid_layout.tsx b/packages/kbn-grid-layout/grid/grid_layout.tsx index c6bbd94dabe56..c3f9521503107 100644 --- a/packages/kbn-grid-layout/grid/grid_layout.tsx +++ b/packages/kbn-grid-layout/grid/grid_layout.tsx @@ -7,69 +7,110 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import React from 'react'; - -import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; +import { cloneDeep } from 'lodash'; +import React, { forwardRef, useEffect, useImperativeHandle, useState } from 'react'; +import { combineLatest, distinctUntilChanged, filter, map, pairwise, skip } from 'rxjs'; import { GridHeightSmoother } from './grid_height_smoother'; import { GridRow } from './grid_row'; -import { GridLayoutData, GridSettings } from './types'; +import { GridLayoutApi, GridLayoutData, GridSettings } from './types'; +import { useGridLayoutApi } from './use_grid_layout_api'; import { useGridLayoutEvents } from './use_grid_layout_events'; import { useGridLayoutState } from './use_grid_layout_state'; +import { isLayoutEqual } from './utils/equality_checks'; -export const GridLayout = ({ - getCreationOptions, - renderPanelContents, -}: { +interface GridLayoutProps { getCreationOptions: () => { initialLayout: GridLayoutData; gridSettings: GridSettings }; renderPanelContents: (panelId: string) => React.ReactNode; -}) => { - const { gridLayoutStateManager, setDimensionsRef } = useGridLayoutState({ - getCreationOptions, - }); - useGridLayoutEvents({ gridLayoutStateManager }); + onLayoutChange: (newLayout: GridLayoutData) => void; +} + +export const GridLayout = forwardRef( + ({ getCreationOptions, renderPanelContents, onLayoutChange }, ref) => { + const { gridLayoutStateManager, setDimensionsRef } = useGridLayoutState({ + getCreationOptions, + }); + useGridLayoutEvents({ gridLayoutStateManager }); + + const gridLayoutApi = useGridLayoutApi({ gridLayoutStateManager }); + useImperativeHandle(ref, () => gridLayoutApi, [gridLayoutApi]); + + const [rowCount, setRowCount] = useState( + gridLayoutStateManager.gridLayout$.getValue().length + ); + + useEffect(() => { + /** + * The only thing that should cause the entire layout to re-render is adding a new row; + * this subscription ensures this by updating the `rowCount` state when it changes. + */ + const rowCountSubscription = gridLayoutStateManager.gridLayout$ + .pipe( + skip(1), // we initialized `rowCount` above, so skip the initial emit + map((newLayout) => newLayout.length), + distinctUntilChanged() + ) + .subscribe((newRowCount) => { + setRowCount(newRowCount); + }); + + const onLayoutChangeSubscription = combineLatest([ + gridLayoutStateManager.gridLayout$, + gridLayoutStateManager.interactionEvent$, + ]) + .pipe( + // if an interaction event is happening, then ignore any "draft" layout changes + filter(([_, event]) => !Boolean(event)), + // once no interaction event, create pairs of "old" and "new" layouts for comparison + map(([layout]) => layout), + pairwise() + ) + .subscribe(([layoutBefore, layoutAfter]) => { + if (!isLayoutEqual(layoutBefore, layoutAfter)) { + onLayoutChange(layoutAfter); + } + }); - const [gridLayout, runtimeSettings, interactionEvent] = useBatchedPublishingSubjects( - gridLayoutStateManager.gridLayout$, - gridLayoutStateManager.runtimeSettings$, - gridLayoutStateManager.interactionEvent$ - ); + return () => { + rowCountSubscription.unsubscribe(); + onLayoutChangeSubscription.unsubscribe(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - return ( - <> - -
{ - setDimensionsRef(divElement); - }} - > - {gridLayout.map((rowData, rowIndex) => { - return ( - { - const currentLayout = gridLayoutStateManager.gridLayout$.value; - currentLayout[rowIndex].isCollapsed = !currentLayout[rowIndex].isCollapsed; - gridLayoutStateManager.gridLayout$.next(currentLayout); - }} - setInteractionEvent={(nextInteractionEvent) => { - if (nextInteractionEvent?.type === 'drop') { - gridLayoutStateManager.activePanel$.next(undefined); - } - gridLayoutStateManager.interactionEvent$.next(nextInteractionEvent); - }} - ref={(element) => (gridLayoutStateManager.rowRefs.current[rowIndex] = element)} - /> - ); - })} -
-
- - ); -}; + return ( + <> + +
{ + setDimensionsRef(divElement); + }} + > + {Array.from({ length: rowCount }, (_, rowIndex) => { + return ( + { + const newLayout = cloneDeep(gridLayoutStateManager.gridLayout$.value); + newLayout[rowIndex].isCollapsed = !newLayout[rowIndex].isCollapsed; + gridLayoutStateManager.gridLayout$.next(newLayout); + }} + setInteractionEvent={(nextInteractionEvent) => { + if (!nextInteractionEvent) { + gridLayoutStateManager.activePanel$.next(undefined); + } + gridLayoutStateManager.interactionEvent$.next(nextInteractionEvent); + }} + ref={(element) => (gridLayoutStateManager.rowRefs.current[rowIndex] = element)} + /> + ); + })} +
+
+ + ); + } +); diff --git a/packages/kbn-grid-layout/grid/grid_panel.tsx b/packages/kbn-grid-layout/grid/grid_panel.tsx index fbe34c4b68e15..822cb2328c4a5 100644 --- a/packages/kbn-grid-layout/grid/grid_panel.tsx +++ b/packages/kbn-grid-layout/grid/grid_panel.tsx @@ -7,7 +7,8 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import React, { forwardRef } from 'react'; +import React, { forwardRef, useEffect, useMemo } from 'react'; +import { combineLatest, skip } from 'rxjs'; import { EuiIcon, @@ -20,108 +21,192 @@ import { import { css } from '@emotion/react'; import { euiThemeVars } from '@kbn/ui-theme'; -import { GridPanelData, PanelInteractionEvent } from './types'; +import { GridLayoutStateManager, PanelInteractionEvent } from './types'; export const GridPanel = forwardRef< HTMLDivElement, { - panelData: GridPanelData; - activePanelId: string | undefined; + panelId: string; + rowIndex: number; renderPanelContents: (panelId: string) => React.ReactNode; interactionStart: ( - type: PanelInteractionEvent['type'], + type: PanelInteractionEvent['type'] | 'drop', e: React.MouseEvent ) => void; + gridLayoutStateManager: GridLayoutStateManager; } ->(({ activePanelId, panelData, renderPanelContents, interactionStart }, panelRef) => { - const { euiTheme } = useEuiTheme(); - const thisPanelActive = activePanelId === panelData.id; +>( + ( + { panelId, rowIndex, renderPanelContents, interactionStart, gridLayoutStateManager }, + panelRef + ) => { + const { euiTheme } = useEuiTheme(); - return ( -
- - {/* drag handle */} -
interactionStart('drag', e)} - onMouseUp={(e) => interactionStart('drop', e)} - > - -
- {/* Resize handle */} -
interactionStart('resize', e)} - onMouseUp={(e) => interactionStart('drop', e)} - css={css` - right: 0; - bottom: 0; - opacity: 0; - margin: -2px; - position: absolute; - width: ${euiThemeVars.euiSizeL}; - height: ${euiThemeVars.euiSizeL}; - transition: opacity 0.2s, border 0.2s; - border-radius: 7px 0 7px 0; - border-bottom: 2px solid ${euiThemeVars.euiColorSuccess}; - border-right: 2px solid ${euiThemeVars.euiColorSuccess}; - :hover { - background-color: ${transparentize(euiThemeVars.euiColorSuccess, 0.05)}; - cursor: se-resize; + /** Set initial styles based on state at mount to prevent styles from "blipping" */ + const initialStyles = useMemo(() => { + const initialPanel = gridLayoutStateManager.gridLayout$.getValue()[rowIndex].panels[panelId]; + return css` + grid-column-start: ${initialPanel.column + 1}; + grid-column-end: ${initialPanel.column + 1 + initialPanel.width}; + grid-row-start: ${initialPanel.row + 1}; + grid-row-end: ${initialPanel.row + 1 + initialPanel.height}; + `; + }, [gridLayoutStateManager, rowIndex, panelId]); + + useEffect( + () => { + /** Update the styles of the panel via a subscription to prevent re-renders */ + const styleSubscription = combineLatest([ + gridLayoutStateManager.activePanel$, + gridLayoutStateManager.gridLayout$, + gridLayoutStateManager.runtimeSettings$, + ]) + .pipe(skip(1)) // skip the first emit because the `initialStyles` will take care of it + .subscribe(([activePanel, gridLayout, runtimeSettings]) => { + const ref = gridLayoutStateManager.panelRefs.current[rowIndex][panelId]; + const panel = gridLayout[rowIndex].panels[panelId]; + if (!ref || !panel) return; + + const currentInteractionEvent = gridLayoutStateManager.interactionEvent$.getValue(); + if (panelId === activePanel?.id) { + // if the current panel is active, give it fixed positioning depending on the interaction event + const { position: draggingPosition } = activePanel; + + ref.style.zIndex = `${euiThemeVars.euiZModal}`; + if (currentInteractionEvent?.type === 'resize') { + // if the current panel is being resized, ensure it is not shrunk past the size of a single cell + ref.style.width = `${Math.max( + draggingPosition.right - draggingPosition.left, + runtimeSettings.columnPixelWidth + )}px`; + ref.style.height = `${Math.max( + draggingPosition.bottom - draggingPosition.top, + runtimeSettings.rowHeight + )}px`; + + // undo any "lock to grid" styles **except** for the top left corner, which stays locked + ref.style.gridColumnStart = `${panel.column + 1}`; + ref.style.gridRowStart = `${panel.row + 1}`; + ref.style.gridColumnEnd = ``; + ref.style.gridRowEnd = ``; + } else { + // if the current panel is being dragged, render it with a fixed position + size + ref.style.position = 'fixed'; + ref.style.left = `${draggingPosition.left}px`; + ref.style.top = `${draggingPosition.top}px`; + ref.style.width = `${draggingPosition.right - draggingPosition.left}px`; + ref.style.height = `${draggingPosition.bottom - draggingPosition.top}px`; + + // undo any "lock to grid" styles + ref.style.gridColumnStart = ``; + ref.style.gridRowStart = ``; + ref.style.gridColumnEnd = ``; + ref.style.gridRowEnd = ``; + } + } else { + ref.style.zIndex = '0'; + + // if the panel is not being dragged and/or resized, undo any fixed position styles + ref.style.position = ''; + ref.style.left = ``; + ref.style.top = ``; + ref.style.width = ``; + ref.style.height = ``; + + // and render the panel locked to the grid + ref.style.gridColumnStart = `${panel.column + 1}`; + ref.style.gridColumnEnd = `${panel.column + 1 + panel.width}`; + ref.style.gridRowStart = `${panel.row + 1}`; + ref.style.gridRowEnd = `${panel.row + 1 + panel.height}`; } - `} - /> -
{ + styleSubscription.unsubscribe(); + }; + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + + return ( +
+ - {renderPanelContents(panelData.id)} -
- -
- ); -}); + {/* drag handle */} +
interactionStart('drag', e)} + onMouseUp={(e) => interactionStart('drop', e)} + > + +
+ {/* Resize handle */} +
interactionStart('resize', e)} + onMouseUp={(e) => interactionStart('drop', e)} + css={css` + right: 0; + bottom: 0; + opacity: 0; + margin: -2px; + position: absolute; + width: ${euiThemeVars.euiSizeL}; + height: ${euiThemeVars.euiSizeL}; + transition: opacity 0.2s, border 0.2s; + border-radius: 7px 0 7px 0; + border-bottom: 2px solid ${euiThemeVars.euiColorSuccess}; + border-right: 2px solid ${euiThemeVars.euiColorSuccess}; + :hover { + opacity: 1; + background-color: ${transparentize(euiThemeVars.euiColorSuccess, 0.05)}; + cursor: se-resize; + } + `} + /> +
+ {renderPanelContents(panelId)} +
+ +
+ ); + } +); diff --git a/packages/kbn-grid-layout/grid/grid_row.tsx b/packages/kbn-grid-layout/grid/grid_row.tsx index 917f661c91740..ff97b32efcdbc 100644 --- a/packages/kbn-grid-layout/grid/grid_row.tsx +++ b/packages/kbn-grid-layout/grid/grid_row.tsx @@ -7,41 +7,23 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import React, { forwardRef, useMemo, useRef } from 'react'; +import React, { forwardRef, useCallback, useEffect, useMemo, useState } from 'react'; +import { combineLatest, map, pairwise, skip } from 'rxjs'; import { EuiButtonIcon, EuiFlexGroup, EuiSpacer, EuiTitle, transparentize } from '@elastic/eui'; import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; import { euiThemeVars } from '@kbn/ui-theme'; -import { useStateFromPublishingSubject } from '@kbn/presentation-publishing'; +import { DragPreview } from './drag_preview'; import { GridPanel } from './grid_panel'; -import { - GridLayoutStateManager, - GridRowData, - PanelInteractionEvent, - RuntimeGridSettings, -} from './types'; - -const gridColor = transparentize(euiThemeVars.euiColorSuccess, 0.2); -const getGridBackgroundCSS = (settings: RuntimeGridSettings) => { - const { gutterSize, columnPixelWidth, rowHeight } = settings; - return css` - background-position: top -${gutterSize / 2}px left -${gutterSize / 2}px; - background-size: ${columnPixelWidth + gutterSize}px ${rowHeight + gutterSize}px; - background-image: linear-gradient(to right, ${gridColor} 1px, transparent 1px), - linear-gradient(to bottom, ${gridColor} 1px, transparent 1px); - `; -}; +import { GridLayoutStateManager, GridRowData, PanelInteractionEvent } from './types'; export const GridRow = forwardRef< HTMLDivElement, { rowIndex: number; - rowData: GridRowData; toggleIsCollapsed: () => void; - targetRowIndex: number | undefined; - runtimeSettings: RuntimeGridSettings; renderPanelContents: (panelId: string) => React.ReactNode; setInteractionEvent: (interactionData?: PanelInteractionEvent) => void; gridLayoutStateManager: GridLayoutStateManager; @@ -49,10 +31,7 @@ export const GridRow = forwardRef< >( ( { - rowData, rowIndex, - targetRowIndex, - runtimeSettings, toggleIsCollapsed, renderPanelContents, setInteractionEvent, @@ -60,19 +39,121 @@ export const GridRow = forwardRef< }, gridRef ) => { - const dragPreviewRef = useRef(null); - const activePanel = useStateFromPublishingSubject(gridLayoutStateManager.activePanel$); + const currentRow = gridLayoutStateManager.gridLayout$.value[rowIndex]; + const [panelIds, setPanelIds] = useState(Object.keys(currentRow.panels)); + const [rowTitle, setRowTitle] = useState(currentRow.title); + const [isCollapsed, setIsCollapsed] = useState(currentRow.isCollapsed); - const { gutterSize, columnCount, rowHeight } = runtimeSettings; - const isGridTargeted = activePanel?.id && targetRowIndex === rowIndex; + const getRowCount = useCallback( + (row: GridRowData) => { + const maxRow = Object.values(row.panels).reduce((acc, panel) => { + return Math.max(acc, panel.row + panel.height); + }, 0); + return maxRow || 1; + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [rowIndex] + ); + + /** Set initial styles based on state at mount to prevent styles from "blipping" */ + const initialStyles = useMemo(() => { + const initialRow = gridLayoutStateManager.gridLayout$.getValue()[rowIndex]; + const runtimeSettings = gridLayoutStateManager.runtimeSettings$.getValue(); + const { gutterSize, columnCount, rowHeight } = runtimeSettings; + + return css` + gap: ${gutterSize}px; + grid-template-columns: repeat( + ${columnCount}, + calc((100% - ${gutterSize * (columnCount - 1)}px) / ${columnCount}) + ); + grid-template-rows: repeat(${getRowCount(initialRow)}, ${rowHeight}px); + `; + }, [gridLayoutStateManager, getRowCount, rowIndex]); + + useEffect( + () => { + /** Update the styles of the grid row via a subscription to prevent re-renders */ + const styleSubscription = combineLatest([ + gridLayoutStateManager.interactionEvent$, + gridLayoutStateManager.gridLayout$, + gridLayoutStateManager.runtimeSettings$, + ]) + .pipe(skip(1)) // skip the first emit because the `initialStyles` will take care of it + .subscribe(([interactionEvent, gridLayout, runtimeSettings]) => { + const rowRef = gridLayoutStateManager.rowRefs.current[rowIndex]; + if (!rowRef) return; + + const { gutterSize, rowHeight, columnPixelWidth } = runtimeSettings; + + rowRef.style.gridTemplateRows = `repeat(${getRowCount( + gridLayout[rowIndex] + )}, ${rowHeight}px)`; - // calculate row count based on the number of rows needed to fit all panels - const rowCount = useMemo(() => { - const maxRow = Object.values(rowData.panels).reduce((acc, panel) => { - return Math.max(acc, panel.row + panel.height); - }, 0); - return maxRow || 1; - }, [rowData]); + const targetRow = interactionEvent?.targetRowIndex; + if (rowIndex === targetRow && interactionEvent) { + // apply "targetted row" styles + const gridColor = transparentize(euiThemeVars.euiColorSuccess, 0.2); + rowRef.style.backgroundPosition = `top -${gutterSize / 2}px left -${ + gutterSize / 2 + }px`; + rowRef.style.backgroundSize = ` ${columnPixelWidth + gutterSize}px ${ + rowHeight + gutterSize + }px`; + rowRef.style.backgroundImage = `linear-gradient(to right, ${gridColor} 1px, transparent 1px), + linear-gradient(to bottom, ${gridColor} 1px, transparent 1px)`; + rowRef.style.backgroundColor = `${transparentize( + euiThemeVars.euiColorSuccess, + 0.05 + )}`; + } else { + // undo any "targetted row" styles + rowRef.style.backgroundPosition = ``; + rowRef.style.backgroundSize = ``; + rowRef.style.backgroundImage = ``; + rowRef.style.backgroundColor = `transparent`; + } + }); + + /** + * The things that should trigger a re-render are title, collapsed state, and panel ids - panel positions + * are being controlled via CSS styles, so they do not need to trigger a re-render. This subscription ensures + * that the row will re-render when one of those three things changes. + */ + const rowStateSubscription = gridLayoutStateManager.gridLayout$ + .pipe( + map((gridLayout) => { + return { + title: gridLayout[rowIndex].title, + isCollapsed: gridLayout[rowIndex].isCollapsed, + panelIds: Object.keys(gridLayout[rowIndex].panels), + }; + }), + pairwise() + ) + .subscribe(([oldRowData, newRowData]) => { + if (oldRowData.title !== newRowData.title) setRowTitle(newRowData.title); + if (oldRowData.isCollapsed !== newRowData.isCollapsed) + setIsCollapsed(newRowData.isCollapsed); + if ( + oldRowData.panelIds.length !== newRowData.panelIds.length || + !( + oldRowData.panelIds.every((p) => newRowData.panelIds.includes(p)) && + newRowData.panelIds.every((p) => oldRowData.panelIds.includes(p)) + ) + ) { + setPanelIds(newRowData.panelIds); + } + }); + + return () => { + styleSubscription.unsubscribe(); + rowStateSubscription.unsubscribe(); + }; + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [rowIndex] + ); return ( <> @@ -85,91 +166,67 @@ export const GridRow = forwardRef< aria-label={i18n.translate('kbnGridLayout.row.toggleCollapse', { defaultMessage: 'Toggle collapse', })} - iconType={rowData.isCollapsed ? 'arrowRight' : 'arrowDown'} + iconType={isCollapsed ? 'arrowRight' : 'arrowDown'} onClick={toggleIsCollapsed} /> -

{rowData.title}

+

{rowTitle}

)} - {!rowData.isCollapsed && ( + {!isCollapsed && (
- {Object.values(rowData.panels).map((panelData) => ( + {panelIds.map((panelId) => ( { e.preventDefault(); e.stopPropagation(); - const panelRef = gridLayoutStateManager.panelRefs.current[rowIndex][panelData.id]; + const panelRef = gridLayoutStateManager.panelRefs.current[rowIndex][panelId]; if (!panelRef) return; const panelRect = panelRef.getBoundingClientRect(); - setInteractionEvent({ - type, - id: panelData.id, - panelDiv: panelRef, - targetRowIndex: rowIndex, - mouseOffsets: { - top: e.clientY - panelRect.top, - left: e.clientX - panelRect.left, - right: e.clientX - panelRect.right, - bottom: e.clientY - panelRect.bottom, - }, - }); + if (type === 'drop') { + setInteractionEvent(undefined); + } else { + setInteractionEvent({ + type, + id: panelId, + panelDiv: panelRef, + targetRowIndex: rowIndex, + mouseOffsets: { + top: e.clientY - panelRect.top, + left: e.clientX - panelRect.left, + right: e.clientX - panelRect.right, + bottom: e.clientY - panelRect.bottom, + }, + }); + } }} ref={(element) => { if (!gridLayoutStateManager.panelRefs.current[rowIndex]) { gridLayoutStateManager.panelRefs.current[rowIndex] = {}; } - gridLayoutStateManager.panelRefs.current[rowIndex][panelData.id] = element; + gridLayoutStateManager.panelRefs.current[rowIndex][panelId] = element; }} /> ))} - {/* render the drag preview if this row is currently being targetted */} - {isGridTargeted && ( -
- )} +
)} diff --git a/packages/kbn-grid-layout/grid/types.ts b/packages/kbn-grid-layout/grid/types.ts index 3a88eeb33baba..004669e69b186 100644 --- a/packages/kbn-grid-layout/grid/types.ts +++ b/packages/kbn-grid-layout/grid/types.ts @@ -9,11 +9,13 @@ import { BehaviorSubject } from 'rxjs'; import type { ObservedSize } from 'use-resize-observer/polyfilled'; + +import { SerializableRecord } from '@kbn/utility-types'; + export interface GridCoordinate { column: number; row: number; } - export interface GridRect extends GridCoordinate { width: number; height: number; @@ -57,8 +59,9 @@ export interface ActivePanel { } export interface GridLayoutStateManager { - gridDimensions$: BehaviorSubject; gridLayout$: BehaviorSubject; + + gridDimensions$: BehaviorSubject; runtimeSettings$: BehaviorSubject; activePanel$: BehaviorSubject; interactionEvent$: BehaviorSubject; @@ -74,7 +77,7 @@ export interface PanelInteractionEvent { /** * The type of interaction being performed. */ - type: 'drag' | 'resize' | 'drop'; + type: 'drag' | 'resize'; /** * The id of the panel being interacted with. @@ -102,3 +105,29 @@ export interface PanelInteractionEvent { bottom: number; }; } + +/** + * The external API provided through the GridLayout component + */ +export interface GridLayoutApi { + addPanel: (panelId: string, placementSettings: PanelPlacementSettings) => void; + removePanel: (panelId: string) => void; + replacePanel: (oldPanelId: string, newPanelId: string) => void; + + getPanelCount: () => number; + serializeState: () => GridLayoutData & SerializableRecord; +} + +// TODO: Remove from Dashboard plugin as part of https://github.com/elastic/kibana/issues/190446 +export enum PanelPlacementStrategy { + /** Place on the very top of the grid layout, add the height of this panel to all other panels. */ + placeAtTop = 'placeAtTop', + /** Look for the smallest y and x value where the default panel will fit. */ + findTopLeftMostOpenSpace = 'findTopLeftMostOpenSpace', +} + +export interface PanelPlacementSettings { + strategy?: PanelPlacementStrategy; + height: number; + width: number; +} diff --git a/packages/kbn-grid-layout/grid/use_grid_layout_api.ts b/packages/kbn-grid-layout/grid/use_grid_layout_api.ts new file mode 100644 index 0000000000000..1a950ee934174 --- /dev/null +++ b/packages/kbn-grid-layout/grid/use_grid_layout_api.ts @@ -0,0 +1,109 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { useMemo } from 'react'; +import { cloneDeep } from 'lodash'; + +import { SerializableRecord } from '@kbn/utility-types'; + +import { GridLayoutApi, GridLayoutData, GridLayoutStateManager } from './types'; +import { compactGridRow } from './utils/resolve_grid_row'; +import { runPanelPlacementStrategy } from './utils/run_panel_placement'; + +export const useGridLayoutApi = ({ + gridLayoutStateManager, +}: { + gridLayoutStateManager: GridLayoutStateManager; +}): GridLayoutApi => { + const api: GridLayoutApi = useMemo(() => { + return { + addPanel: (panelId, placementSettings) => { + const currentLayout = gridLayoutStateManager.gridLayout$.getValue(); + const [firstRow, ...rest] = currentLayout; // currently, only adding panels to the first row is supported + const { columnCount: gridColumnCount } = gridLayoutStateManager.runtimeSettings$.getValue(); + const nextRow = runPanelPlacementStrategy( + firstRow, + { + id: panelId, + width: placementSettings.width, + height: placementSettings.height, + }, + gridColumnCount, + placementSettings?.strategy + ); + gridLayoutStateManager.gridLayout$.next([nextRow, ...rest]); + }, + + removePanel: (panelId) => { + const currentLayout = gridLayoutStateManager.gridLayout$.getValue(); + + // find the row where the panel exists and delete it from the corresponding panels object + let rowIndex = 0; + let updatedPanels; + for (rowIndex; rowIndex < currentLayout.length; rowIndex++) { + const row = currentLayout[rowIndex]; + if (Object.keys(row.panels).includes(panelId)) { + updatedPanels = { ...row.panels }; // prevent mutation of original panel object + delete updatedPanels[panelId]; + break; + } + } + + // if the panels were updated (i.e. the panel was successfully found and deleted), update the layout + if (updatedPanels) { + const newLayout = cloneDeep(currentLayout); + newLayout[rowIndex] = compactGridRow({ + ...newLayout[rowIndex], + panels: updatedPanels, + }); + gridLayoutStateManager.gridLayout$.next(newLayout); + } + }, + + replacePanel: (oldPanelId, newPanelId) => { + const currentLayout = gridLayoutStateManager.gridLayout$.getValue(); + + // find the row where the panel exists and update its ID to trigger a re-render + let rowIndex = 0; + let updatedPanels; + for (rowIndex; rowIndex < currentLayout.length; rowIndex++) { + const row = { ...currentLayout[rowIndex] }; + if (Object.keys(row.panels).includes(oldPanelId)) { + updatedPanels = { ...row.panels }; // prevent mutation of original panel object + const oldPanel = updatedPanels[oldPanelId]; + delete updatedPanels[oldPanelId]; + updatedPanels[newPanelId] = { ...oldPanel, id: newPanelId }; + break; + } + } + + // if the panels were updated (i.e. the panel was successfully found and replaced), update the layout + if (updatedPanels) { + const newLayout = cloneDeep(currentLayout); + newLayout[rowIndex].panels = updatedPanels; + gridLayoutStateManager.gridLayout$.next(newLayout); + } + }, + + getPanelCount: () => { + return gridLayoutStateManager.gridLayout$.getValue().reduce((prev, row) => { + return prev + Object.keys(row.panels).length; + }, 0); + }, + + serializeState: () => { + const currentLayout = gridLayoutStateManager.gridLayout$.getValue(); + return cloneDeep(currentLayout) as GridLayoutData & SerializableRecord; + }, + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return api; +}; diff --git a/packages/kbn-grid-layout/grid/use_grid_layout_events.ts b/packages/kbn-grid-layout/grid/use_grid_layout_events.ts index dfbd013d3642a..22dde2fe68ced 100644 --- a/packages/kbn-grid-layout/grid/use_grid_layout_events.ts +++ b/packages/kbn-grid-layout/grid/use_grid_layout_events.ts @@ -7,20 +7,11 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import deepEqual from 'fast-deep-equal'; import { useEffect, useRef } from 'react'; - -import { resolveGridRow } from './resolve_grid_row'; -import { GridLayoutStateManager, GridPanelData } from './types'; - -export const isGridDataEqual = (a?: GridPanelData, b?: GridPanelData) => { - return ( - a?.id === b?.id && - a?.column === b?.column && - a?.row === b?.row && - a?.width === b?.width && - a?.height === b?.height - ); -}; +import { resolveGridRow } from './utils/resolve_grid_row'; +import { GridPanelData, GridLayoutStateManager } from './types'; +import { isGridDataEqual } from './utils/equality_checks'; export const useGridLayoutEvents = ({ gridLayoutStateManager, @@ -36,7 +27,7 @@ export const useGridLayoutEvents = ({ useEffect(() => { const { runtimeSettings$, interactionEvent$, gridLayout$ } = gridLayoutStateManager; const calculateUserEvent = (e: Event) => { - if (!interactionEvent$.value || interactionEvent$.value.type === 'drop') return; + if (!interactionEvent$.value) return; e.preventDefault(); e.stopPropagation(); @@ -121,6 +112,7 @@ export const useGridLayoutEvents = ({ maxColumn ); const targetRow = Math.max(Math.round(localYCoordinate / (rowHeight + gutterSize)), 0); + const requestedGridData = { ...currentGridData }; if (isResize) { requestedGridData.width = Math.max(targetColumn - requestedGridData.column, 1); @@ -154,8 +146,9 @@ export const useGridLayoutEvents = ({ const resolvedOriginGrid = resolveGridRow(originGrid); nextLayout[lastRowIndex] = resolvedOriginGrid; } - - gridLayout$.next(nextLayout); + if (!deepEqual(currentLayout, nextLayout)) { + gridLayout$.next(nextLayout); + } } }; diff --git a/packages/kbn-grid-layout/grid/use_grid_layout_state.ts b/packages/kbn-grid-layout/grid/use_grid_layout_state.ts index cdb99a9ebbfd0..fe657ae253107 100644 --- a/packages/kbn-grid-layout/grid/use_grid_layout_state.ts +++ b/packages/kbn-grid-layout/grid/use_grid_layout_state.ts @@ -7,10 +7,11 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { i18n } from '@kbn/i18n'; import { useEffect, useMemo, useRef } from 'react'; -import { BehaviorSubject, combineLatest, debounceTime, map, retry } from 'rxjs'; +import { BehaviorSubject, debounceTime } from 'rxjs'; + import useResizeObserver, { type ObservedSize } from 'use-resize-observer/polyfilled'; + import { ActivePanel, GridLayoutData, @@ -43,10 +44,14 @@ export const useGridLayoutState = ({ ...gridSettings, columnPixelWidth: 0, }); + const panelIds$ = new BehaviorSubject( + initialLayout.map(({ panels }) => Object.keys(panels)) + ); return { rowRefs, panelRefs, + panelIds$, gridLayout$, activePanel$, gridDimensions$, @@ -67,117 +72,12 @@ export const useGridLayoutState = ({ const columnPixelWidth = (elementWidth - gridSettings.gutterSize * (gridSettings.columnCount - 1)) / gridSettings.columnCount; - gridLayoutStateManager.runtimeSettings$.next({ ...gridSettings, columnPixelWidth }); - }); - - /** - * on layout change, update the styles of every panel so that it renders as expected - */ - const onLayoutChangeSubscription = combineLatest([ - gridLayoutStateManager.gridLayout$, - gridLayoutStateManager.activePanel$, - ]) - .pipe( - map(([gridLayout, activePanel]) => { - // wait for all panel refs to be ready before continuing - for (let rowIndex = 0; rowIndex < gridLayout.length; rowIndex++) { - const currentRow = gridLayout[rowIndex]; - Object.keys(currentRow.panels).forEach((key) => { - const panelRef = gridLayoutStateManager.panelRefs.current[rowIndex][key]; - if (!panelRef && !currentRow.isCollapsed) { - throw new Error( - i18n.translate('kbnGridLayout.panelRefNotFoundError', { - defaultMessage: 'Panel reference does not exist', // the retry will catch this error - }) - ); - } - }); - } - return { gridLayout, activePanel }; - }), - retry({ delay: 10 }) // retry until panel references all exist - ) - .subscribe(({ gridLayout, activePanel }) => { - const runtimeSettings = gridLayoutStateManager.runtimeSettings$.getValue(); - const currentInteractionEvent = gridLayoutStateManager.interactionEvent$.getValue(); - for (let rowIndex = 0; rowIndex < gridLayout.length; rowIndex++) { - if (activePanel && rowIndex !== currentInteractionEvent?.targetRowIndex) { - /** - * If there is an interaction event happening but the current row is not being targetted, it - * does not need to be re-rendered; so, skip setting the panel styles of this row. - * - * If there is **no** interaction event, then this is the initial render so the styles of every - * panel should be initialized; so, don't skip setting the panel styles. - */ - continue; - } - - // re-render the targetted row - const currentRow = gridLayout[rowIndex]; - Object.keys(currentRow.panels).forEach((key) => { - const panel = currentRow.panels[key]; - const panelRef = gridLayoutStateManager.panelRefs.current[rowIndex][key]; - if (!panelRef) { - return; - } - - const isResize = currentInteractionEvent?.type === 'resize'; - if (panel.id === activePanel?.id) { - // if the current panel is active, give it fixed positioning depending on the interaction event - const { position: draggingPosition } = activePanel; - - if (isResize) { - // if the current panel is being resized, ensure it is not shrunk past the size of a single cell - panelRef.style.width = `${Math.max( - draggingPosition.right - draggingPosition.left, - runtimeSettings.columnPixelWidth - )}px`; - panelRef.style.height = `${Math.max( - draggingPosition.bottom - draggingPosition.top, - runtimeSettings.rowHeight - )}px`; - - // undo any "lock to grid" styles **except** for the top left corner, which stays locked - panelRef.style.gridColumnStart = `${panel.column + 1}`; - panelRef.style.gridRowStart = `${panel.row + 1}`; - panelRef.style.gridColumnEnd = ``; - panelRef.style.gridRowEnd = ``; - } else { - // if the current panel is being dragged, render it with a fixed position + size - panelRef.style.position = 'fixed'; - panelRef.style.left = `${draggingPosition.left}px`; - panelRef.style.top = `${draggingPosition.top}px`; - panelRef.style.width = `${draggingPosition.right - draggingPosition.left}px`; - panelRef.style.height = `${draggingPosition.bottom - draggingPosition.top}px`; - - // undo any "lock to grid" styles - panelRef.style.gridColumnStart = ``; - panelRef.style.gridRowStart = ``; - panelRef.style.gridColumnEnd = ``; - panelRef.style.gridRowEnd = ``; - } - } else { - // if the panel is not being dragged and/or resized, undo any fixed position styles - panelRef.style.position = ''; - panelRef.style.left = ``; - panelRef.style.top = ``; - panelRef.style.width = ``; - panelRef.style.height = ``; - - // and render the panel locked to the grid - panelRef.style.gridColumnStart = `${panel.column + 1}`; - panelRef.style.gridColumnEnd = `${panel.column + 1 + panel.width}`; - panelRef.style.gridRowStart = `${panel.row + 1}`; - panelRef.style.gridRowEnd = `${panel.row + 1 + panel.height}`; - } - }); - } + gridLayoutStateManager.runtimeSettings$.next({ ...gridSettings, columnPixelWidth }); }); return () => { resizeSubscription.unsubscribe(); - onLayoutChangeSubscription.unsubscribe(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/packages/kbn-grid-layout/grid/utils/equality_checks.ts b/packages/kbn-grid-layout/grid/utils/equality_checks.ts new file mode 100644 index 0000000000000..6771baa3a1030 --- /dev/null +++ b/packages/kbn-grid-layout/grid/utils/equality_checks.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { GridLayoutData, GridPanelData } from '../types'; + +export const isGridDataEqual = (a?: GridPanelData, b?: GridPanelData) => { + return ( + a?.id === b?.id && + a?.column === b?.column && + a?.row === b?.row && + a?.width === b?.width && + a?.height === b?.height + ); +}; + +export const isLayoutEqual = (a: GridLayoutData, b: GridLayoutData) => { + if (a.length !== b.length) return false; + + let isEqual = true; + for (let rowIndex = 0; rowIndex < a.length && isEqual; rowIndex++) { + const rowA = a[rowIndex]; + const rowB = b[rowIndex]; + + isEqual = + rowA.title === rowB.title && + rowA.isCollapsed === rowB.isCollapsed && + Object.keys(rowA.panels).length === Object.keys(rowB.panels).length; + + if (isEqual) { + for (const panelKey of Object.keys(rowA.panels)) { + isEqual = isGridDataEqual(rowA.panels[panelKey], rowB.panels[panelKey]); + if (!isEqual) break; + } + } + } + + return isEqual; +}; diff --git a/packages/kbn-grid-layout/grid/resolve_grid_row.ts b/packages/kbn-grid-layout/grid/utils/resolve_grid_row.ts similarity index 96% rename from packages/kbn-grid-layout/grid/resolve_grid_row.ts rename to packages/kbn-grid-layout/grid/utils/resolve_grid_row.ts index 4c300336c7617..3037a52c27c69 100644 --- a/packages/kbn-grid-layout/grid/resolve_grid_row.ts +++ b/packages/kbn-grid-layout/grid/utils/resolve_grid_row.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { GridPanelData, GridRowData } from './types'; +import { GridPanelData, GridRowData } from '../types'; const collides = (panelA: GridPanelData, panelB: GridPanelData) => { if (panelA.id === panelB.id) return false; // same panel @@ -57,7 +57,7 @@ const getKeysInOrder = (rowData: GridRowData, draggedId?: string): string[] => { }); }; -const compactGridRow = (originalLayout: GridRowData) => { +export const compactGridRow = (originalLayout: GridRowData) => { const nextRowData = { ...originalLayout, panels: { ...originalLayout.panels } }; // compact all vertical space. const sortedKeysAfterMove = getKeysInOrder(nextRowData); diff --git a/packages/kbn-grid-layout/grid/utils/run_panel_placement.ts b/packages/kbn-grid-layout/grid/utils/run_panel_placement.ts new file mode 100644 index 0000000000000..69ecddd1f5ffb --- /dev/null +++ b/packages/kbn-grid-layout/grid/utils/run_panel_placement.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ +import { i18n } from '@kbn/i18n'; +import { GridRowData } from '../..'; +import { GridPanelData, PanelPlacementStrategy } from '../types'; +import { compactGridRow, resolveGridRow } from './resolve_grid_row'; + +export const runPanelPlacementStrategy = ( + originalRowData: GridRowData, + newPanel: Omit, + columnCount: number, + strategy: PanelPlacementStrategy = PanelPlacementStrategy.findTopLeftMostOpenSpace +): GridRowData => { + const nextRowData = { ...originalRowData, panels: { ...originalRowData.panels } }; // prevent mutation of original row object + switch (strategy) { + case PanelPlacementStrategy.placeAtTop: + // move all other panels down by the height of the new panel to make room for the new panel + Object.keys(nextRowData.panels).forEach((key) => { + const panel = nextRowData.panels[key]; + panel.row += newPanel.height; + }); + + // some panels might need to be pushed back up because they are now floating - so, compact the row + return compactGridRow({ + ...nextRowData, + // place the new panel at the top left corner, since there is now space + panels: { ...nextRowData.panels, [newPanel.id]: { ...newPanel, row: 0, column: 0 } }, + }); + + case PanelPlacementStrategy.findTopLeftMostOpenSpace: + // find the max row + let maxRow = -1; + const currentPanelsArray = Object.values(nextRowData.panels); + currentPanelsArray.forEach((panel) => { + maxRow = Math.max(panel.row + panel.height, maxRow); + }); + + // handle case of empty grid by placing the panel at the top left corner + if (maxRow < 0) { + return { + ...nextRowData, + panels: { [newPanel.id]: { ...newPanel, row: 0, column: 0 } }, + }; + } + + // find a spot in the grid where the entire panel will fit + const { row, column } = (() => { + // create a 2D array representation of the grid filled with zeros + const grid = new Array(maxRow); + for (let y = 0; y < maxRow; y++) { + grid[y] = new Array(columnCount).fill(0); + } + + // fill in the 2D array with ones wherever a panel is + currentPanelsArray.forEach((panel) => { + for (let x = panel.column; x < panel.column + panel.width; x++) { + for (let y = panel.row; y < panel.row + panel.height; y++) { + grid[y][x] = 1; + } + } + }); + + // now find the first empty spot where there are enough zeros (unoccupied spaces) to fit the whole panel + for (let y = 0; y < maxRow; y++) { + for (let x = 0; x < columnCount; x++) { + if (grid[y][x] === 1) { + // space is filled, so skip this spot + continue; + } else { + for (let h = y; h < Math.min(y + newPanel.height, maxRow); h++) { + for (let w = x; w < Math.min(x + newPanel.width, columnCount); w++) { + const spaceIsEmpty = grid[h][w] === 0; + const fitsPanelWidth = w === x + newPanel.width - 1; + // if the panel is taller than any other panel in the current grid, it can still fit in the space, hence + // we check the minimum of maxY and the panel height. + const fitsPanelHeight = h === Math.min(y + newPanel.height - 1, maxRow - 1); + + if (spaceIsEmpty && fitsPanelWidth && fitsPanelHeight) { + // found an empty space where the entire panel will fit + return { column: x, row: y }; + } else if (grid[h][w] === 1) { + // x, y is already occupied - break out of the loop and move on to the next starting point + break; + } + } + } + } + } + } + + return { column: 0, row: maxRow }; + })(); + + // some panels might need to be pushed down to accomodate the height of the new panel; + // so, resolve the entire row to remove any potential collisions + return resolveGridRow({ + ...nextRowData, + // place the new panel at the top left corner, since there is now space + panels: { ...nextRowData.panels, [newPanel.id]: { ...newPanel, row, column } }, + }); + + default: + throw new Error( + i18n.translate('kbnGridLayout.panelPlacement.unknownStrategyError', { + defaultMessage: 'Unknown panel placement strategy: {strategy}', + values: { strategy }, + }) + ); + } +}; diff --git a/packages/kbn-grid-layout/index.ts b/packages/kbn-grid-layout/index.ts index 009b74573e895..924369fe5ab4c 100644 --- a/packages/kbn-grid-layout/index.ts +++ b/packages/kbn-grid-layout/index.ts @@ -8,4 +8,12 @@ */ export { GridLayout } from './grid/grid_layout'; -export type { GridLayoutData, GridPanelData, GridRowData, GridSettings } from './grid/types'; +export type { + GridLayoutApi, + GridLayoutData, + GridPanelData, + GridRowData, + GridSettings, +} from './grid/types'; + +export { isLayoutEqual } from './grid/utils/equality_checks'; diff --git a/packages/kbn-grid-layout/tsconfig.json b/packages/kbn-grid-layout/tsconfig.json index 5a1888db0dc64..14ab38ba76ba9 100644 --- a/packages/kbn-grid-layout/tsconfig.json +++ b/packages/kbn-grid-layout/tsconfig.json @@ -17,8 +17,8 @@ "target/**/*" ], "kbn_references": [ - "@kbn/presentation-publishing", "@kbn/ui-theme", "@kbn/i18n", + "@kbn/utility-types", ] } diff --git a/packages/kbn-language-documentation/src/sections/esql_documentation_sections.tsx b/packages/kbn-language-documentation/src/sections/esql_documentation_sections.tsx index 66c93fc0f7787..59f9b999c8a5f 100644 --- a/packages/kbn-language-documentation/src/sections/esql_documentation_sections.tsx +++ b/packages/kbn-language-documentation/src/sections/esql_documentation_sections.tsx @@ -19,7 +19,7 @@ export const initialSection = ( @@ -77,7 +77,7 @@ ES|QL can access the following metadata fields: Use the \`METADATA\` directive to enable metadata fields: \`\`\` -FROM index [METADATA _index, _id] +FROM index METADATA _index, _id \`\`\` Metadata fields are only available if the source of the data is an index. Consequently, \`FROM\` is the only source commands that supports the \`METADATA\` directive. @@ -85,7 +85,7 @@ Metadata fields are only available if the source of the data is an index. Conseq Once enabled, the fields are then available to subsequent processing commands, just like the other index fields: \`\`\` -FROM ul_logs, apps [METADATA _index, _version] +FROM ul_logs, apps METADATA _index, _version | WHERE id IN (13, 14) AND _version == 1 | EVAL key = CONCAT(_index, "_", TO_STR(id)) | SORT id, _index @@ -95,7 +95,7 @@ FROM ul_logs, apps [METADATA _index, _version] Also, similar to the index fields, once an aggregation is performed, a metadata field will no longer be accessible to subsequent commands, unless used as grouping field: \`\`\` -FROM employees [METADATA _index, _id] +FROM employees METADATA _index, _id | STATS max = MAX(emp_no) BY _index \`\`\` `, @@ -114,7 +114,7 @@ FROM employees [METADATA _index, _id] markdownContent={i18n.translate('languageDocumentation.documentationESQL.row.markdown', { defaultMessage: `### ROW The \`ROW\` source command produces a row with one or more columns with values that you specify. This can be useful for testing. - + \`\`\` ROW a = 1, b = "two", c = null \`\`\` @@ -207,7 +207,7 @@ ROW a = "1953-01-23T12:15:00Z - some text - 127.0.0.1" markdownContent={i18n.translate('languageDocumentation.documentationESQL.drop.markdown', { defaultMessage: `### DROP Use \`DROP\` to remove columns from a table: - + \`\`\` FROM employees | DROP height @@ -347,7 +347,7 @@ ROW a = "12 15.5 15.6 true" The \`KEEP\` command enables you to specify what columns are returned and the order in which they are returned. To limit the columns that are returned, use a comma-separated list of column names. The columns are returned in the specified order: - + \`\`\` FROM employees | KEEP first_name, last_name, height @@ -384,7 +384,7 @@ FROM employees { defaultMessage: `### LIMIT The \`LIMIT\` processing command enables you to limit the number of rows: - + \`\`\` FROM employees | LIMIT 5 @@ -407,7 +407,7 @@ FROM employees 'languageDocumentation.documentationESQL.mvExpand.markdown', { defaultMessage: `### MV_EXPAND -The \`MV_EXPAND\` processing command expands multivalued fields into one row per value, duplicating other fields: +The \`MV_EXPAND\` processing command expands multivalued fields into one row per value, duplicating other fields: \`\`\` ROW a=[1,2,3], b="b", j=["a","b"] | MV_EXPAND a @@ -607,7 +607,7 @@ FROM employees { defaultMessage: `### WHERE Use \`WHERE\` to produce a table that contains all the rows from the input table for which the provided condition evaluates to \`true\`: - + \`\`\` FROM employees | KEEP first_name, last_name, still_hired @@ -654,7 +654,7 @@ export const groupingFunctions = { defaultMessage: `### BUCKET Creates groups of values - buckets - out of a datetime or numeric input. The size of the buckets can either be provided directly, or chosen based on a recommended count and values range. -\`BUCKET\` works in two modes: +\`BUCKET\` works in two modes: 1. Where the size of the bucket is computed based on a buckets count recommendation (four parameters) and a range. 2. Where the bucket size is provided directly (two parameters). diff --git a/packages/kbn-management/settings/setting_ids/index.ts b/packages/kbn-management/settings/setting_ids/index.ts index 551e99e4ef131..b146be6f6e252 100644 --- a/packages/kbn-management/settings/setting_ids/index.ts +++ b/packages/kbn-management/settings/setting_ids/index.ts @@ -83,7 +83,6 @@ export const DISCOVER_SAMPLE_SIZE_ID = 'discover:sampleSize'; export const DISCOVER_SEARCH_FIELDS_FROM_SOURCE_ID = 'discover:searchFieldsFromSource'; export const DISCOVER_SEARCH_ON_PAGE_LOAD_ID = 'discover:searchOnPageLoad'; export const DISCOVER_SHOW_FIELD_STATISTICS_ID = 'discover:showFieldStatistics'; -export const DISCOVER_SHOW_LEGACY_FIELD_TOP_VALUES_ID = 'discover:showLegacyFieldTopValues'; export const DISCOVER_SHOW_MULTI_FIELDS_ID = 'discover:showMultiFields'; export const DISCOVER_SORT_DEFAULT_ORDER_ID = 'discover:sort:defaultOrder'; export const DOC_TABLE_HIDE_TIME_COLUMNS_ID = 'doc_table:hideTimeColumn'; diff --git a/packages/kbn-monaco/src/esql/worker/esql_worker.ts b/packages/kbn-monaco/src/esql/worker/esql_worker.ts index 82d6c75f8f621..18ce300acfc2f 100644 --- a/packages/kbn-monaco/src/esql/worker/esql_worker.ts +++ b/packages/kbn-monaco/src/esql/worker/esql_worker.ts @@ -43,7 +43,7 @@ export class ESQLWorker implements BaseWorkerDefinition { } getAst(text: string | undefined) { - const rawAst = parse(text); + const rawAst = parse(text, { withFormatting: true }); return { ast: rawAst.root.commands, errors: rawAst.errors.map(inlineToMonacoErrors), diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index 96a17a6ae7229..b5da9566878e1 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -280,6 +280,14 @@ export function getWebpackConfig( plugins: ['@babel/plugin-transform-logical-assignment-operators'], }, }, + { + test: /node_modules[\/\\]launchdarkly[^\/\\]+[\/\\].*.js$/, + loaders: 'babel-loader', + options: { + envName: worker.dist ? 'production' : 'development', + presets: [BABEL_PRESET], + }, + }, { test: /\.(html|md|txt|tmpl)$/, use: { diff --git a/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap b/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap index fef935624ae64..1c1f5bed02a0e 100644 --- a/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap +++ b/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap @@ -44,7 +44,7 @@ Object { "paths": Object { "/foo/{id}": Object { "get": Object { - "operationId": "%2Ffoo%2F%7Bid%7D#0", + "operationId": "get-foo-id", "parameters": Array [ Object { "description": "The version of the API to use", @@ -138,7 +138,7 @@ Object { "/bar": Object { "get": Object { "deprecated": true, - "operationId": "%2Fbar#0", + "operationId": "get-bar", "parameters": Array [ Object { "description": "The version of the API to use", @@ -231,7 +231,7 @@ OK response oas-test-version-2", "/foo/{id}/{path*}": Object { "delete": Object { "description": "route description", - "operationId": "%2Ffoo%2F%7Bid%7D%2F%7Bpath*%7D#2", + "operationId": "delete-foo-id-path", "parameters": Array [ Object { "description": "The version of the API to use", @@ -269,7 +269,7 @@ OK response oas-test-version-2", }, "get": Object { "description": "route description", - "operationId": "%2Ffoo%2F%7Bid%7D%2F%7Bpath*%7D#0", + "operationId": "get-foo-id-path", "parameters": Array [ Object { "description": "The version of the API to use", @@ -415,7 +415,7 @@ OK response oas-test-version-2", }, "post": Object { "description": "route description", - "operationId": "%2Ffoo%2F%7Bid%7D%2F%7Bpath*%7D#1", + "operationId": "post-foo-id-path", "parameters": Array [ Object { "description": "The version of the API to use", @@ -573,7 +573,7 @@ OK response oas-test-version-2", "/no-xsrf/{id}/{path*}": Object { "post": Object { "deprecated": true, - "operationId": "%2Fno-xsrf%2F%7Bid%7D%2F%7Bpath*%7D#1", + "operationId": "post-no-xsrf-id-path-2", "parameters": Array [ Object { "description": "The version of the API to use", @@ -725,7 +725,7 @@ Object { "paths": Object { "/recursive": Object { "get": Object { - "operationId": "%2Frecursive#0", + "operationId": "get-recursive", "parameters": Array [ Object { "description": "The version of the API to use", @@ -808,7 +808,7 @@ Object { "paths": Object { "/foo/{id}": Object { "get": Object { - "operationId": "%2Ffoo%2F%7Bid%7D#0", + "operationId": "get-foo-id", "parameters": Array [ Object { "description": "The version of the API to use", @@ -846,7 +846,7 @@ Object { }, "/test": Object { "get": Object { - "operationId": "%2Ftest#0", + "operationId": "get-test", "parameters": Array [ Object { "description": "The version of the API to use", diff --git a/packages/kbn-router-to-openapispec/src/extract_authz_description.test.ts b/packages/kbn-router-to-openapispec/src/extract_authz_description.test.ts index 8da2324e68f02..308f0a7686597 100644 --- a/packages/kbn-router-to-openapispec/src/extract_authz_description.test.ts +++ b/packages/kbn-router-to-openapispec/src/extract_authz_description.test.ts @@ -33,7 +33,9 @@ describe('extractAuthzDescription', () => { }, }; const description = extractAuthzDescription(routeSecurity); - expect(description).toBe('[Authz] Route required privileges: ALL of [manage_spaces].'); + expect(description).toBe( + '[Required authorization] Route required privileges: ALL of [manage_spaces].' + ); }); it('should return route authz description for privilege groups', () => { @@ -44,7 +46,9 @@ describe('extractAuthzDescription', () => { }, }; const description = extractAuthzDescription(routeSecurity); - expect(description).toBe('[Authz] Route required privileges: ALL of [console].'); + expect(description).toBe( + '[Required authorization] Route required privileges: ALL of [console].' + ); } { const routeSecurity: RouteSecurity = { @@ -58,7 +62,7 @@ describe('extractAuthzDescription', () => { }; const description = extractAuthzDescription(routeSecurity); expect(description).toBe( - '[Authz] Route required privileges: ANY of [manage_spaces OR taskmanager].' + '[Required authorization] Route required privileges: ANY of [manage_spaces OR taskmanager].' ); } { @@ -74,7 +78,7 @@ describe('extractAuthzDescription', () => { }; const description = extractAuthzDescription(routeSecurity); expect(description).toBe( - '[Authz] Route required privileges: ALL of [console, filesManagement] AND ANY of [manage_spaces OR taskmanager].' + '[Required authorization] Route required privileges: ALL of [console, filesManagement] AND ANY of [manage_spaces OR taskmanager].' ); } }); diff --git a/packages/kbn-router-to-openapispec/src/extract_authz_description.ts b/packages/kbn-router-to-openapispec/src/extract_authz_description.ts index 4cd6875913780..7979188f2641e 100644 --- a/packages/kbn-router-to-openapispec/src/extract_authz_description.ts +++ b/packages/kbn-router-to-openapispec/src/extract_authz_description.ts @@ -56,5 +56,5 @@ export const extractAuthzDescription = (routeSecurity: InternalRouteSecurity | u return `Route required privileges: ${getPrivilegesDescription(allRequired, anyRequired)}.`; }; - return `[Authz] ${getDescriptionForRoute()}`; + return `[Required authorization] ${getDescriptionForRoute()}`; }; diff --git a/packages/kbn-router-to-openapispec/src/generate_oas.test.fixture.ts b/packages/kbn-router-to-openapispec/src/generate_oas.test.fixture.ts index b3f20da38915b..f4ba66f992134 100644 --- a/packages/kbn-router-to-openapispec/src/generate_oas.test.fixture.ts +++ b/packages/kbn-router-to-openapispec/src/generate_oas.test.fixture.ts @@ -35,7 +35,7 @@ export const sharedOas = { get: { deprecated: true, 'x-discontinued': 'route discontinued version or date', - operationId: '%2Fbar#0', + operationId: 'get-bar', parameters: [ { description: 'The version of the API to use', @@ -154,7 +154,7 @@ export const sharedOas = { '/foo/{id}/{path*}': { get: { description: 'route description', - operationId: '%2Ffoo%2F%7Bid%7D%2F%7Bpath*%7D#0', + operationId: 'get-foo-id-path', parameters: [ { description: 'The version of the API to use', @@ -278,7 +278,7 @@ export const sharedOas = { }, post: { description: 'route description', - operationId: '%2Ffoo%2F%7Bid%7D%2F%7Bpath*%7D#1', + operationId: 'post-foo-id-path', parameters: [ { description: 'The version of the API to use', diff --git a/packages/kbn-router-to-openapispec/src/generate_oas.ts b/packages/kbn-router-to-openapispec/src/generate_oas.ts index 8bc3333193624..9c7423147721b 100644 --- a/packages/kbn-router-to-openapispec/src/generate_oas.ts +++ b/packages/kbn-router-to-openapispec/src/generate_oas.ts @@ -10,10 +10,9 @@ import type { CoreVersionedRouter, Router } from '@kbn/core-http-router-server-internal'; import type { OpenAPIV3 } from 'openapi-types'; import { OasConverter } from './oas_converter'; -import { createOperationIdCounter } from './operation_id_counter'; import { processRouter } from './process_router'; import { processVersionedRouter } from './process_versioned_router'; -import { buildGlobalTags } from './util'; +import { buildGlobalTags, createOpIdGenerator } from './util'; export const openApiVersion = '3.0.0'; @@ -40,8 +39,8 @@ export const generateOpenApiDocument = ( ): OpenAPIV3.Document => { const { filters } = opts; const converter = new OasConverter(); - const getOpId = createOperationIdCounter(); const paths: OpenAPIV3.PathsObject = {}; + const getOpId = createOpIdGenerator(); for (const router of appRouters.routers) { const result = processRouter(router, converter, getOpId, filters); Object.assign(paths, result.paths); diff --git a/packages/kbn-router-to-openapispec/src/operation_id_counter.test.ts b/packages/kbn-router-to-openapispec/src/operation_id_counter.test.ts deleted file mode 100644 index dbc4bf5956d69..0000000000000 --- a/packages/kbn-router-to-openapispec/src/operation_id_counter.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { createOperationIdCounter } from './operation_id_counter'; - -test('empty case', () => { - const opIdCounter = createOperationIdCounter(); - expect(opIdCounter('')).toBe('#0'); -}); - -test('other cases', () => { - const opIdCounter = createOperationIdCounter(); - const tests = [ - ['/', '%2F#0'], - ['/api/cool', '%2Fapi%2Fcool#0'], - ['/api/cool', '%2Fapi%2Fcool#1'], - ['/api/cool', '%2Fapi%2Fcool#2'], - ['/api/cool/{variable}', '%2Fapi%2Fcool%2F%7Bvariable%7D#0'], - ['/api/cool/{optionalVariable?}', '%2Fapi%2Fcool%2F%7BoptionalVariable%3F%7D#0'], - ['/api/cool/{optionalVariable?}', '%2Fapi%2Fcool%2F%7BoptionalVariable%3F%7D#1'], - ]; - - tests.forEach(([input, expected]) => { - expect(opIdCounter(input)).toBe(expected); - }); -}); diff --git a/packages/kbn-router-to-openapispec/src/operation_id_counter.ts b/packages/kbn-router-to-openapispec/src/operation_id_counter.ts deleted file mode 100644 index 2d576b1ca67c3..0000000000000 --- a/packages/kbn-router-to-openapispec/src/operation_id_counter.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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export type OperationIdCounter = (name: string) => string; - -export const createOperationIdCounter = () => { - const operationIdCounters = new Map(); - return (name: string): string => { - name = encodeURIComponent(name); - // Aliases an operationId to ensure it is unique across - // multiple method+path combinations sharing a name. - // "search" -> "search#0", "search#1", etc. - const operationIdCount = operationIdCounters.get(name) ?? 0; - const aliasedName = name + '#' + operationIdCount.toString(); - operationIdCounters.set(name, operationIdCount + 1); - return aliasedName; - }; -}; diff --git a/packages/kbn-router-to-openapispec/src/process_router.test.ts b/packages/kbn-router-to-openapispec/src/process_router.test.ts index 96a10b25d648a..2ce135a378789 100644 --- a/packages/kbn-router-to-openapispec/src/process_router.test.ts +++ b/packages/kbn-router-to-openapispec/src/process_router.test.ts @@ -10,9 +10,9 @@ import { schema } from '@kbn/config-schema'; import { Router } from '@kbn/core-http-router-server-internal'; import { OasConverter } from './oas_converter'; -import { createOperationIdCounter } from './operation_id_counter'; import { extractResponses, processRouter } from './process_router'; import { type InternalRouterRoute } from './type'; +import { createOpIdGenerator } from './util'; describe('extractResponses', () => { let oasConverter: OasConverter; @@ -86,18 +86,21 @@ describe('processRouter', () => { const testRouter = { getRoutes: () => [ { + method: 'get', path: '/foo', options: { access: 'internal', deprecated: true, discontinued: 'discontinued router' }, handler: jest.fn(), validationSchemas: { request: { body: schema.object({}) } }, }, { + method: 'get', path: '/bar', options: {}, handler: jest.fn(), validationSchemas: { request: { body: schema.object({}) } }, }, { + method: 'get', path: '/baz', options: {}, handler: jest.fn(), @@ -121,31 +124,55 @@ describe('processRouter', () => { }, }, }, + { + path: '/quux', + method: 'post', + options: { + description: 'This a test route description.', + }, + handler: jest.fn(), + validationSchemas: { request: { body: schema.object({}) } }, + security: { + authz: { + requiredPrivileges: [ + 'manage_spaces', + { + allRequired: ['taskmanager'], + anyRequired: ['console'], + }, + ], + }, + }, + }, ], } as unknown as Router; it('only provides routes for version 2023-10-31', () => { - const result1 = processRouter(testRouter, new OasConverter(), createOperationIdCounter(), { + const result1 = processRouter(testRouter, new OasConverter(), createOpIdGenerator(), { version: '2023-10-31', }); - expect(Object.keys(result1.paths!)).toHaveLength(4); + expect(Object.keys(result1.paths!)).toHaveLength(5); - const result2 = processRouter(testRouter, new OasConverter(), createOperationIdCounter(), { + const result2 = processRouter(testRouter, new OasConverter(), createOpIdGenerator(), { version: '2024-10-31', }); expect(Object.keys(result2.paths!)).toHaveLength(0); }); it('updates description with privileges required', () => { - const result = processRouter(testRouter, new OasConverter(), createOperationIdCounter(), { + const result = processRouter(testRouter, new OasConverter(), createOpIdGenerator(), { version: '2023-10-31', }); expect(result.paths['/qux']?.post).toBeDefined(); expect(result.paths['/qux']?.post?.description).toEqual( - '[Authz] Route required privileges: ALL of [manage_spaces, taskmanager] AND ANY of [console].' + '[Required authorization] Route required privileges: ALL of [manage_spaces, taskmanager] AND ANY of [console].' + ); + + expect(result.paths['/quux']?.post?.description).toEqual( + 'This a test route description.

[Required authorization] Route required privileges: ALL of [manage_spaces, taskmanager] AND ANY of [console].' ); }); }); diff --git a/packages/kbn-router-to-openapispec/src/process_router.ts b/packages/kbn-router-to-openapispec/src/process_router.ts index cb55af3735b34..b808f9bed84d5 100644 --- a/packages/kbn-router-to-openapispec/src/process_router.ts +++ b/packages/kbn-router-to-openapispec/src/process_router.ts @@ -24,8 +24,8 @@ import { mergeResponseContent, prepareRoutes, setXState, + GetOpId, } from './util'; -import type { OperationIdCounter } from './operation_id_counter'; import type { GenerateOpenApiDocumentOptionsFilters } from './generate_oas'; import type { CustomOperationObject, InternalRouterRoute } from './type'; import { extractAuthzDescription } from './extract_authz_description'; @@ -33,7 +33,7 @@ import { extractAuthzDescription } from './extract_authz_description'; export const processRouter = ( appRouter: Router, converter: OasConverter, - getOpId: OperationIdCounter, + getOpId: GetOpId, filters?: GenerateOpenApiDocumentOptionsFilters ) => { const paths: OpenAPIV3.PathsObject = {}; @@ -64,11 +64,13 @@ export const processRouter = ( parameters.push(...pathObjects, ...queryObjects); } - let description = ''; + let description = `${route.options.description ?? ''}`; if (route.security) { const authzDescription = extractAuthzDescription(route.security); - description = `${route.options.description ?? ''}${authzDescription ?? ''}`; + description += `${route.options.description && authzDescription ? `

` : ''}${ + authzDescription ?? '' + }`; } const hasDeprecations = !!route.options.deprecated; @@ -77,7 +79,6 @@ export const processRouter = ( summary: route.options.summary ?? '', tags: route.options.tags ? extractTags(route.options.tags) : [], ...(description ? { description } : {}), - ...(route.options.description ? { description: route.options.description } : {}), ...(hasDeprecations ? { deprecated: true } : {}), ...(route.options.discontinued ? { 'x-discontinued': route.options.discontinued } : {}), requestBody: !!validationSchemas?.body @@ -91,7 +92,7 @@ export const processRouter = ( : undefined, responses: extractResponses(route, converter), parameters, - operationId: getOpId(route.path), + operationId: getOpId({ path: route.path, method: route.method }), }; setXState(route.options.availability, operation); diff --git a/packages/kbn-router-to-openapispec/src/process_versioned_router.test.ts b/packages/kbn-router-to-openapispec/src/process_versioned_router.test.ts index 3738c207f1f78..b7a4827e4f365 100644 --- a/packages/kbn-router-to-openapispec/src/process_versioned_router.test.ts +++ b/packages/kbn-router-to-openapispec/src/process_versioned_router.test.ts @@ -11,13 +11,13 @@ import { schema } from '@kbn/config-schema'; import type { CoreVersionedRouter } from '@kbn/core-http-router-server-internal'; import { get } from 'lodash'; import { OasConverter } from './oas_converter'; -import { createOperationIdCounter } from './operation_id_counter'; import { processVersionedRouter, extractVersionedResponses, extractVersionedRequestBodies, } from './process_versioned_router'; import { VersionedRouterRoute } from '@kbn/core-http-server'; +import { createOpIdGenerator } from './util'; let oasConverter: OasConverter; beforeEach(() => { @@ -125,7 +125,7 @@ describe('processVersionedRouter', () => { const baseCase = processVersionedRouter( { getRoutes: () => [createTestRoute()] } as unknown as CoreVersionedRouter, new OasConverter(), - createOperationIdCounter(), + createOpIdGenerator(), {} ); @@ -137,7 +137,7 @@ describe('processVersionedRouter', () => { const filteredCase = processVersionedRouter( { getRoutes: () => [createTestRoute()] } as unknown as CoreVersionedRouter, new OasConverter(), - createOperationIdCounter(), + createOpIdGenerator(), { version: '2023-10-31' } ); expect(Object.keys(get(filteredCase, 'paths["/foo"].get.responses.200.content')!)).toEqual([ @@ -149,7 +149,7 @@ describe('processVersionedRouter', () => { const results = processVersionedRouter( { getRoutes: () => [createTestRoute()] } as unknown as CoreVersionedRouter, new OasConverter(), - createOperationIdCounter(), + createOpIdGenerator(), {} ); expect(results.paths['/foo']).toBeDefined(); @@ -157,7 +157,7 @@ describe('processVersionedRouter', () => { expect(results.paths['/foo']!.get).toBeDefined(); expect(results.paths['/foo']!.get!.description).toBe( - '[Authz] Route required privileges: ALL of [manage_spaces].' + 'This is a test route description.

[Required authorization] Route required privileges: ALL of [manage_spaces].' ); }); }); @@ -176,6 +176,7 @@ const createTestRoute: () => VersionedRouterRoute = () => ({ requiredPrivileges: ['manage_spaces'], }, }, + description: 'This is a test route description.', }, handlers: [ { diff --git a/packages/kbn-router-to-openapispec/src/process_versioned_router.ts b/packages/kbn-router-to-openapispec/src/process_versioned_router.ts index 5dad5677c94ac..eab2dfef78a21 100644 --- a/packages/kbn-router-to-openapispec/src/process_versioned_router.ts +++ b/packages/kbn-router-to-openapispec/src/process_versioned_router.ts @@ -18,7 +18,6 @@ import { extractAuthzDescription } from './extract_authz_description'; import type { GenerateOpenApiDocumentOptionsFilters } from './generate_oas'; import type { OasConverter } from './oas_converter'; import { isReferenceObject } from './oas_converter/common'; -import type { OperationIdCounter } from './operation_id_counter'; import { prepareRoutes, getPathParameters, @@ -30,12 +29,13 @@ import { mergeResponseContent, getXsrfHeaderForMethod, setXState, + GetOpId, } from './util'; export const processVersionedRouter = ( appRouter: CoreVersionedRouter, converter: OasConverter, - getOpId: OperationIdCounter, + getOpId: GetOpId, filters?: GenerateOpenApiDocumentOptionsFilters ) => { const routes = prepareRoutes(appRouter.getRoutes(), filters); @@ -90,12 +90,13 @@ export const processVersionedRouter = ( ...queryObjects, ]; } - - let description = ''; + let description = `${route.options.description ?? ''}`; if (route.options.security) { const authzDescription = extractAuthzDescription(route.options.security); - description = `${route.options.description ?? ''}${authzDescription ?? ''}`; + description += `${route.options.description && authzDescription ? '

' : ''}${ + authzDescription ?? '' + }`; } const hasBody = Boolean(extractValidationSchemaFromVersionedHandler(handler)?.request?.body); @@ -107,7 +108,6 @@ export const processVersionedRouter = ( summary: route.options.summary ?? '', tags: route.options.options?.tags ? extractTags(route.options.options.tags) : [], ...(description ? { description } : {}), - ...(route.options.description ? { description: route.options.description } : {}), ...(hasDeprecations ? { deprecated: true } : {}), ...(route.options.discontinued ? { 'x-discontinued': route.options.discontinued } : {}), requestBody: hasBody @@ -121,7 +121,7 @@ export const processVersionedRouter = ( ? extractVersionedResponse(handler, converter, contentType) : extractVersionedResponses(route, converter, contentType), parameters, - operationId: getOpId(route.path), + operationId: getOpId({ path: route.path, method: route.method }), }; setXState(route.options.options?.availability, operation); diff --git a/packages/kbn-router-to-openapispec/src/util.test.ts b/packages/kbn-router-to-openapispec/src/util.test.ts index abbb605df79e5..f9692e57e1f50 100644 --- a/packages/kbn-router-to-openapispec/src/util.test.ts +++ b/packages/kbn-router-to-openapispec/src/util.test.ts @@ -15,6 +15,8 @@ import { mergeResponseContent, prepareRoutes, getPathParameters, + createOpIdGenerator, + GetOpId, } from './util'; import { assignToPaths, extractTags } from './util'; @@ -260,3 +262,83 @@ describe('getPathParameters', () => { expect(getPathParameters(input)).toEqual(output); }); }); + +describe('createOpIdGenerator', () => { + let getOpId: GetOpId; + beforeEach(() => { + getOpId = createOpIdGenerator(); + }); + test('empty', () => { + expect(() => getOpId({ method: '', path: '/asd' })).toThrow(/Must provide method and path/); + expect(() => getOpId({ method: 'get', path: '' })).toThrow(/Must provide method and path/); + expect(() => getOpId({ method: '', path: '' })).toThrow(/Must provide method and path/); + }); + test('disambiguate', () => { + expect(getOpId({ method: 'get', path: '/test' })).toBe('get-test'); + expect(getOpId({ method: 'get', path: '/test' })).toBe('get-test-2'); + expect(getOpId({ method: 'get', path: '/test' })).toBe('get-test-3'); + expect(getOpId({ method: 'get', path: '/test' })).toBe('get-test-4'); + }); + test.each([ + { input: { method: 'GET', path: '/api/file' }, output: 'get-file' }, + { input: { method: 'GET', path: '///api/file///' }, output: 'get-file' }, + { input: { method: 'POST', path: '/internal/api/file' }, output: 'post-file' }, + { input: { method: 'PUT', path: '/internal/file' }, output: 'put-file' }, + { input: { method: 'Put', path: 'fOO/fILe' }, output: 'put-foo-file' }, + { + input: { method: 'delete', path: '/api/my/really/cool/domain/resource' }, + output: 'delete-my-really-cool-domain-resource', + }, + { + input: { + method: 'delete', + path: '/api/my/really/cool/domain/resource', + }, + output: 'delete-my-really-cool-domain-resource', + }, + { + input: { + method: 'get', + path: '/api/my/resource/{id}', + }, + output: 'get-my-resource-id', + }, + { + input: { + method: 'get', + path: '/api/my/resource/{id}/{type?}', + }, + output: 'get-my-resource-id-type', + }, + { + input: { + method: 'get', + path: '/api/my/resource/{id?}', + }, + output: 'get-my-resource-id', + }, + { + input: { + method: 'get', + path: '/api/my/resource/{path*}', + }, + output: 'get-my-resource-path', + }, + { + input: { + method: 'get', + path: '/api/my/underscore_resource', + }, + output: 'get-my-underscore-resource', + }, + { + input: { + method: 'get', + path: '/api/my/_underscore_resource', + }, + output: 'get-my-underscore-resource', + }, + ])('$input.method $input.path -> $output', ({ input, output }) => { + expect(getOpId(input)).toBe(output); + }); +}); diff --git a/packages/kbn-router-to-openapispec/src/util.ts b/packages/kbn-router-to-openapispec/src/util.ts index beefbebc0aec7..a5718fa92120f 100644 --- a/packages/kbn-router-to-openapispec/src/util.ts +++ b/packages/kbn-router-to-openapispec/src/util.ts @@ -166,10 +166,10 @@ export const getXsrfHeaderForMethod = ( ]; }; -export function setXState( +export const setXState = ( availability: RouteConfigOptions['availability'], operation: CustomOperationObject -): void { +): void => { if (availability) { if (availability.stability === 'experimental') { operation['x-state'] = 'Technical Preview'; @@ -178,4 +178,45 @@ export function setXState( operation['x-state'] = 'Beta'; } } -} +}; + +export type GetOpId = (input: { path: string; method: string }) => string; + +/** + * Best effort to generate operation IDs from route values + */ +export const createOpIdGenerator = (): GetOpId => { + const idMap = new Map(); + return function getOpId({ path, method }) { + if (!method || !path) { + throw new Error( + `Must provide method and path, received: method: "${method}", path: "${path}"` + ); + } + + path = path + .trim() + .replace(/^[\/]+/, '') + .replace(/[\/]+$/, '') + .toLowerCase(); + + const removePrefixes = ['internal/api/', 'internal/', 'api/']; // longest to shortest + for (const prefix of removePrefixes) { + if (path.startsWith(prefix)) { + path = path.substring(prefix.length); + break; + } + } + + path = path + .replace(/[\{\}\?\*]/g, '') // remove special chars + .replace(/[\/_]/g, '-') // everything else to dashes + .replace(/[-]+/g, '-'); // single dashes + + const opId = `${method.toLowerCase()}-${path}`; + + const cachedCount = idMap.get(opId) ?? 0; + idMap.set(opId, cachedCount + 1); + return cachedCount > 0 ? `${opId}-${cachedCount + 1}` : opId; + }; +}; diff --git a/packages/kbn-search-api-panels/components/language_client_panel.tsx b/packages/kbn-search-api-panels/components/language_client_panel.tsx index 2f89c8da7578c..2c07d3118d943 100644 --- a/packages/kbn-search-api-panels/components/language_client_panel.tsx +++ b/packages/kbn-search-api-panels/components/language_client_panel.tsx @@ -62,8 +62,12 @@ export const LanguageClientPanel: React.FC = ({ width={euiTheme.size.xl} /> - -
{language.name}
+ + {language.name} diff --git a/packages/kbn-search-response-warnings/src/extract_warnings.test.ts b/packages/kbn-search-response-warnings/src/extract_warnings.test.ts index cedf0546ae3a4..c5a1352c3d0f9 100644 --- a/packages/kbn-search-response-warnings/src/extract_warnings.test.ts +++ b/packages/kbn-search-response-warnings/src/extract_warnings.test.ts @@ -9,6 +9,7 @@ import { estypes } from '@elastic/elasticsearch'; import type { Start as InspectorStartContract } from '@kbn/inspector-plugin/public'; +import type { ESQLSearchResponse } from '@kbn/es-types'; import type { RequestAdapter } from '@kbn/inspector-plugin/common/adapters/request'; import { extractWarnings } from './extract_warnings'; @@ -108,6 +109,22 @@ describe('extract search response warnings', () => { expect(warnings).toEqual([]); }); + + it('should not include warnings when there is no _clusters or _shards information', () => { + const warnings = extractWarnings( + { + took: 46, + all_columns: [{ name: 'field1', type: 'string' }], + columns: [{ name: 'field1', type: 'string' }], + values: [['value1']], + } as ESQLSearchResponse, + mockInspectorService, + mockRequestAdapter, + 'My request' + ); + + expect(warnings).toEqual([]); + }); }); describe('remote clusters', () => { diff --git a/packages/kbn-search-response-warnings/src/extract_warnings.ts b/packages/kbn-search-response-warnings/src/extract_warnings.ts index 7d53a954f7715..58e963c239b12 100644 --- a/packages/kbn-search-response-warnings/src/extract_warnings.ts +++ b/packages/kbn-search-response-warnings/src/extract_warnings.ts @@ -8,6 +8,7 @@ */ import { estypes } from '@elastic/elasticsearch'; +import type { ESQLSearchResponse } from '@kbn/es-types'; import type { Start as InspectorStartContract, RequestAdapter } from '@kbn/inspector-plugin/public'; import type { SearchResponseWarning } from './types'; @@ -15,7 +16,7 @@ import type { SearchResponseWarning } from './types'; * @internal */ export function extractWarnings( - rawResponse: estypes.SearchResponse, + rawResponse: estypes.SearchResponse | ESQLSearchResponse, inspectorService: InspectorStartContract, requestAdapter: RequestAdapter, requestName: string, @@ -23,11 +24,13 @@ export function extractWarnings( ): SearchResponseWarning[] { const warnings: SearchResponseWarning[] = []; + // ES|QL supports _clusters in case of CCS but doesnt support _shards and timed_out (yet) const isPartial = rawResponse._clusters ? rawResponse._clusters.partial > 0 || rawResponse._clusters.skipped > 0 || rawResponse._clusters.running > 0 - : rawResponse.timed_out || rawResponse._shards.failed > 0; + : ('timed_out' in rawResponse && rawResponse.timed_out) || + ('_shards' in rawResponse && rawResponse._shards.failed > 0); if (isPartial) { warnings.push({ type: 'incomplete', @@ -39,9 +42,10 @@ export function extractWarnings( status: 'partial', indices: '', took: rawResponse.took, - timed_out: rawResponse.timed_out, - _shards: rawResponse._shards, - failures: rawResponse._shards.failures, + timed_out: 'timed_out' in rawResponse && rawResponse.timed_out, + ...('_shards' in rawResponse + ? { _shards: rawResponse._shards, failures: rawResponse._shards.failures } + : {}), }, }, openInInspector: () => { diff --git a/packages/kbn-search-response-warnings/tsconfig.json b/packages/kbn-search-response-warnings/tsconfig.json index 6823ef5abf8a1..1f87892403a61 100644 --- a/packages/kbn-search-response-warnings/tsconfig.json +++ b/packages/kbn-search-response-warnings/tsconfig.json @@ -10,6 +10,7 @@ "@kbn/core", "@kbn/react-kibana-mount", "@kbn/core-i18n-browser", + "@kbn/es-types", ], "exclude": ["target/**/*"] } diff --git a/packages/kbn-test/src/auth/saml_auth.ts b/packages/kbn-test/src/auth/saml_auth.ts index 968788371c827..831161fccfdbe 100644 --- a/packages/kbn-test/src/auth/saml_auth.ts +++ b/packages/kbn-test/src/auth/saml_auth.ts @@ -10,6 +10,7 @@ import { createSAMLResponse as createMockedSAMLResponse } from '@kbn/mock-idp-utils'; import { ToolingLog } from '@kbn/tooling-log'; import axios, { AxiosResponse } from 'axios'; +import util from 'util'; import * as cheerio from 'cheerio'; import { Cookie, parse as parseCookie } from 'tough-cookie'; import Url from 'url'; @@ -263,23 +264,26 @@ export const finishSAMLHandshake = async ({ }) => { const encodedResponse = encodeURIComponent(samlResponse); const url = kbnHost + '/api/security/saml/callback'; + const request = { + url, + method: 'post', + data: `SAMLResponse=${encodedResponse}`, + headers: { + 'content-type': 'application/x-www-form-urlencoded', + ...(sid ? { Cookie: `sid=${sid}` } : {}), + }, + validateStatus: () => true, + maxRedirects: 0, + }; let authResponse: AxiosResponse; try { - authResponse = await axios.request({ - url, - method: 'post', - data: `SAMLResponse=${encodedResponse}`, - headers: { - 'content-type': 'application/x-www-form-urlencoded', - ...(sid ? { Cookie: `sid=${sid}` } : {}), - }, - validateStatus: () => true, - maxRedirects: 0, - }); + authResponse = await axios.request(request); } catch (ex) { log.error('Failed to call SAML callback'); cleanException(url, ex); + // Logging the `Cookie: sid=xxxx` header is safe here since it’s an intermediate, non-authenticated cookie that cannot be reused if leaked. + log.error(`Request sent: ${util.inspect(request)}`); throw ex; } diff --git a/packages/kbn-test/src/auth/session_manager.test.ts b/packages/kbn-test/src/auth/session_manager.test.ts index 98c0181141e54..4b20581eced4c 100644 --- a/packages/kbn-test/src/auth/session_manager.test.ts +++ b/packages/kbn-test/src/auth/session_manager.test.ts @@ -172,7 +172,7 @@ describe('SamlSessionManager', () => { describe('for cloud session', () => { const hostOptions = { protocol: 'https' as 'http' | 'https', - hostname: 'cloud', + hostname: 'my-test-deployment.test.elastic.cloud', username: 'elastic', password: 'changeme', }; @@ -328,4 +328,31 @@ describe('SamlSessionManager', () => { expect(createCloudSAMLSessionMock.mock.calls).toHaveLength(0); }); }); + + describe(`for cloud session with 'isCloud' set to false`, () => { + const hostOptions = { + protocol: 'http' as 'http' | 'https', + hostname: 'my-test-deployment.test.elastic.cloud', + username: 'elastic', + password: 'changeme', + }; + const samlSessionManagerOptions = { + hostOptions, + isCloud: false, + log, + cloudUsersFilePath, + }; + + beforeEach(() => { + jest.resetAllMocks(); + }); + + test('should throw an error when kbnHost points to a Cloud instance', () => { + const kbnHost = `${hostOptions.protocol}://${hostOptions.hostname}`; + expect(() => new SamlSessionManager(samlSessionManagerOptions)).toThrow( + `SamlSessionManager: 'isCloud' was set to false, but 'kbnHost' appears to be a Cloud instance: ${kbnHost} +Set env variable 'TEST_CLOUD=1' to run FTR against your Cloud deployment` + ); + }); + }); }); diff --git a/packages/kbn-test/src/auth/session_manager.ts b/packages/kbn-test/src/auth/session_manager.ts index e376135296bd7..ba411aaa21891 100644 --- a/packages/kbn-test/src/auth/session_manager.ts +++ b/packages/kbn-test/src/auth/session_manager.ts @@ -54,7 +54,6 @@ export class SamlSessionManager { private readonly cloudUsersFilePath: string; constructor(options: SamlSessionManagerOptions) { - this.isCloud = options.isCloud; this.log = options.log; const hostOptionsWithoutAuth = { protocol: options.hostOptions.protocol, @@ -62,6 +61,7 @@ export class SamlSessionManager { port: options.hostOptions.port, }; this.kbnHost = Url.format(hostOptionsWithoutAuth); + this.isCloud = options.isCloud; this.kbnClient = new KbnClient({ log: this.log, url: Url.format({ @@ -73,6 +73,22 @@ export class SamlSessionManager { this.sessionCache = new Map(); this.roleToUserMap = new Map(); this.supportedRoles = options.supportedRoles; + this.validateCloudSetting(); + } + + /** + * Validates if the 'kbnHost' points to Cloud, even if 'isCloud' was set to false + */ + private validateCloudSetting() { + const cloudSubDomains = ['elastic.cloud', 'foundit.no', 'cloud.es.io', 'elastic-cloud.com']; + const isCloudHost = cloudSubDomains.some((domain) => this.kbnHost.endsWith(domain)); + + if (!this.isCloud && isCloudHost) { + throw new Error( + `SamlSessionManager: 'isCloud' was set to false, but 'kbnHost' appears to be a Cloud instance: ${this.kbnHost} +Set env variable 'TEST_CLOUD=1' to run FTR against your Cloud deployment` + ); + } } /** 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 724cf5bc2b25e..964359cdb7ee5 100644 --- a/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts +++ b/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts @@ -11,6 +11,7 @@ import Url from 'url'; import { resolve } from 'path'; import type { ToolingLog } from '@kbn/tooling-log'; import getPort from 'get-port'; +import getopts from 'getopts'; import { REPO_ROOT } from '@kbn/repo-info'; import type { ArtifactLicense, ServerlessProjectType } from '@kbn/es'; import { isServerlessProjectType, extractAndArchiveLogs } from '@kbn/es/src/utils'; @@ -196,12 +197,8 @@ function getESServerlessOptions( (config.get('kbnTestServer.serverArgs') as string[])) || []; - const projectType = kbnServerArgs - .filter((arg) => arg.startsWith('--serverless')) - .reduce((acc, arg) => { - const match = arg.match(/--serverless[=\s](\w+)/); - return acc + (match ? match[1] : ''); - }, '') as ServerlessProjectType; + const options = getopts(kbnServerArgs); + const projectType = options.serverless as ServerlessProjectType; if (!isServerlessProjectType(projectType)) { throw new Error(`Unsupported serverless projectType: ${projectType}`); diff --git a/packages/kbn-test/src/mocha/junit_report_generation.test.js b/packages/kbn-test/src/mocha/junit_report_generation.test.js index caf023a795154..6dbc8bf6cf1f8 100644 --- a/packages/kbn-test/src/mocha/junit_report_generation.test.js +++ b/packages/kbn-test/src/mocha/junit_report_generation.test.js @@ -55,9 +55,12 @@ describe('dev/mocha/junit report generation', () => { const [testsuite] = report.testsuites.testsuite; expect(testsuite.$.time).toMatch(DURATION_REGEX); expect(testsuite.$.timestamp).toMatch(ISO_DATE_SEC_REGEX); - expect(testsuite.$).toEqual({ - 'command-line': - 'node scripts/jest --config=packages/kbn-test/jest.config.js --runInBand --coverage=false --passWithNoTests', + const expectedCommandLine = process.env.CI + ? 'node scripts/jest --config=packages/kbn-test/jest.config.js --runInBand --coverage=false --passWithNoTests' + : 'node node_modules/jest-worker/build/workers/processChild.js'; + + expect(testsuite.$).toMatchObject({ + 'command-line': expectedCommandLine, failures: '2', name: 'test', skipped: '1', diff --git a/packages/kbn-typed-react-router-config/kibana.jsonc b/packages/kbn-typed-react-router-config/kibana.jsonc index 0462d28238890..c004d263a6046 100644 --- a/packages/kbn-typed-react-router-config/kibana.jsonc +++ b/packages/kbn-typed-react-router-config/kibana.jsonc @@ -1,5 +1,5 @@ { "type": "shared-common", "id": "@kbn/typed-react-router-config", - "owner": ["@elastic/obs-knowledge-team", "@elastic/obs-ux-management-team"] + "owner": ["@elastic/obs-knowledge-team", "@elastic/obs-ux-infra_services-team"] } 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 af162996f5f85..9ed3604dcdb8a 100644 --- a/packages/shared-ux/chrome/navigation/__jest__/active_node.test.tsx +++ b/packages/shared-ux/chrome/navigation/__jest__/active_node.test.tsx @@ -76,7 +76,7 @@ describe('Active node', () => { ]; const { findByTestId } = renderNavigation({ - navTreeDef: of({ body: navigationBody }), + navTreeDef: of({ id: 'es', body: navigationBody }), services: { activeNodes$: getActiveNodes$() }, }); 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 15baf09d04dc3..4196dd3eca21c 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 @@ -21,6 +21,7 @@ describe('builds navigation tree', () => { test('render reference UI and build the navigation tree', async () => { const { findByTestId } = renderNavigation({ navTreeDef: of({ + id: 'es', body: [ { id: 'group1', @@ -107,6 +108,7 @@ describe('builds navigation tree', () => { { const { findByTestId, unmount } = renderNavigation({ navTreeDef: of({ + id: 'es', body: [accordionNode], }), services: { navigateToUrl }, @@ -121,6 +123,7 @@ describe('builds navigation tree', () => { { const { findByTestId } = renderNavigation({ navTreeDef: of({ + id: 'es', body: [ { ...accordionNode, @@ -165,6 +168,7 @@ describe('builds navigation tree', () => { // Side nav is collapsed const { queryAllByTestId, unmount } = renderNavigation({ navTreeDef: of({ + id: 'es', body: [nodes], }), services: { isSideNavCollapsed: true }, @@ -180,6 +184,7 @@ describe('builds navigation tree', () => { // Side nav is not collapsed const { queryAllByTestId, unmount } = renderNavigation({ navTreeDef: of({ + id: 'es', body: [nodes], }), services: { isSideNavCollapsed: false }, // No conversion to accordion @@ -195,6 +200,7 @@ describe('builds navigation tree', () => { // Panel opener with a link const { queryAllByTestId, unmount } = renderNavigation({ navTreeDef: of({ + id: 'es', body: [ { ...nodes, @@ -238,6 +244,7 @@ describe('builds navigation tree', () => { const { findByTestId } = renderNavigation({ navTreeDef: of({ + id: 'es', body: [node], }), services: { navigateToUrl, eventTracker: new EventTracker({ reportEvent }) }, @@ -276,6 +283,7 @@ describe('builds navigation tree', () => { const { findByTestId } = renderNavigation({ navTreeDef: of({ + id: 'es', body: [node], }), services: { navigateToUrl }, @@ -290,6 +298,7 @@ describe('builds navigation tree', () => { test('should not render the group if it does not have children', async () => { const navTree: NavigationTreeDefinitionUI = { + id: 'es', body: [ { id: 'root', @@ -338,6 +347,7 @@ describe('builds navigation tree', () => { ]); const navTree: NavigationTreeDefinitionUI = { + id: 'es', body: [{ type: 'recentlyAccessed' }], }; @@ -364,6 +374,7 @@ describe('builds navigation tree', () => { ]); const navTree: NavigationTreeDefinitionUI = { + id: 'es', body: [{ type: 'recentlyAccessed' }], }; diff --git a/packages/shared-ux/chrome/navigation/__jest__/panel.test.tsx b/packages/shared-ux/chrome/navigation/__jest__/panel.test.tsx index 01a1112ca7d1a..59c3463fb7326 100644 --- a/packages/shared-ux/chrome/navigation/__jest__/panel.test.tsx +++ b/packages/shared-ux/chrome/navigation/__jest__/panel.test.tsx @@ -21,6 +21,7 @@ import { renderNavigation } from './utils'; describe('Panel', () => { test('should render group as panel opener', async () => { const navigationTree: NavigationTreeDefinitionUI = { + id: 'es', body: [ { id: 'root', @@ -60,6 +61,7 @@ describe('Panel', () => { test('should not render group if all children are hidden', async () => { const navigationTree: NavigationTreeDefinitionUI = { + id: 'es', body: [ { id: 'root', @@ -146,6 +148,7 @@ describe('Panel', () => { ]); const navTree: NavigationTreeDefinitionUI = { + id: 'es', body: [ { id: 'root', @@ -196,6 +199,7 @@ describe('Panel', () => { describe('auto generated content', () => { test('should rendre block groups with title', async () => { const navTree: NavigationTreeDefinitionUI = { + id: 'es', body: [ { id: 'root', @@ -262,6 +266,7 @@ describe('Panel', () => { test('should rendre block groups without title', async () => { const navTree: NavigationTreeDefinitionUI = { + id: 'es', body: [ { id: 'root', @@ -327,6 +332,7 @@ describe('Panel', () => { test('should rendre accordion groups', async () => { const navTree: NavigationTreeDefinitionUI = { + id: 'es', body: [ { id: 'root', diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/feedback_btn.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/feedback_btn.tsx index 3cc2fca2d8f81..182838d88b5d6 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/feedback_btn.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/feedback_btn.tsx @@ -10,11 +10,20 @@ import { EuiButton, EuiCallOut, useEuiTheme, EuiText, EuiSpacer } from '@elastic/eui'; import React, { FC, useState } from 'react'; import { i18n } from '@kbn/i18n'; +import type { SolutionId } from '@kbn/core-chrome-browser'; -const feedbackUrl = 'https://ela.st/nav-feedback'; +const feedbackUrls: { [id in SolutionId]: string } = { + es: 'https://ela.st/search-nav-feedback', + oblt: 'https://ela.st/o11y-nav-feedback', + security: 'https://ela.st/security-nav-feedback', +}; const FEEDBACK_BTN_KEY = 'core.chrome.sideNav.feedbackBtn'; -export const FeedbackBtn: FC = () => { +interface Props { + solutionId: SolutionId; +} + +export const FeedbackBtn: FC = ({ solutionId }) => { const { euiTheme } = useEuiTheme(); const [showCallOut, setShowCallOut] = useState( sessionStorage.getItem(FEEDBACK_BTN_KEY) !== 'hidden' @@ -26,7 +35,7 @@ export const FeedbackBtn: FC = () => { }; const onClick = () => { - window.open(feedbackUrl, '_blank'); + window.open(feedbackUrls[solutionId], '_blank'); onDismiss(); }; 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 6100dceaa2499..c90ec6a72152f 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/navigation.stories.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/navigation.stories.tsx @@ -91,6 +91,7 @@ const NavigationWrapper: FC, 'c }; const groupExamplesNavigationTree: NavigationTreeDefinitionUI = { + id: 'es', body: [ // My custom project { @@ -257,6 +258,7 @@ export const GroupsExamples = (args: NavigationServices) => { }; const navigationTree: NavigationTreeDefinitionUI = { + id: 'es', body: [ // My custom project { @@ -568,6 +570,7 @@ const panelContentProvider: ContentProvider = (id: string) => { }; const navigationTreeWithPanels: NavigationTreeDefinitionUI = { + id: 'es', body: [ // My custom project { diff --git a/packages/shared-ux/chrome/navigation/src/ui/navigation.tsx b/packages/shared-ux/chrome/navigation/src/ui/navigation.tsx index 688ee1e709e15..80365bd16133f 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/navigation.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/navigation.tsx @@ -52,7 +52,8 @@ const NavigationComp: FC = ({ navigationTree$, dataTestSubj, panelContent useNavigationService(); const activeNodes = useObservable(activeNodes$, []); - const navigationTree = useObservable(navigationTree$, { body: [] }); + const navigationTree = useObservable(navigationTree$, { id: 'es', body: [] }); + const { id: solutionId } = navigationTree; const isFeedbackBtnVisible = useObservable(isFeedbackBtnVisible$, false); const contextValue = useMemo( @@ -95,7 +96,7 @@ const NavigationComp: FC = ({ navigationTree$, dataTestSubj, panelContent {renderNodes(navigationTree.body)} {isFeedbackBtnVisible && ( - + )} diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/incompatible_cluster_routing_allocation.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/incompatible_cluster_routing_allocation.test.ts index 56488f571ef16..8213c880c0fa4 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/incompatible_cluster_routing_allocation.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/incompatible_cluster_routing_allocation.test.ts @@ -121,7 +121,7 @@ describe('incompatible_cluster_routing_allocation', () => { await root.preboot(); await root.setup(); - root.start().catch(() => { + const startPromise = root.start().catch(() => { // Silent catch because the test might be done and call shutdown before starting is completed, causing unwanted thrown errors. }); @@ -165,6 +165,7 @@ describe('incompatible_cluster_routing_allocation', () => { { retryAttempts: 100, retryDelayMs: 500 } ); + await startPromise; // Wait for start phase to complete before shutting down await root.shutdown(); }); }); diff --git a/src/core/server/integration_tests/saved_objects/routes/import.test.ts b/src/core/server/integration_tests/saved_objects/routes/import.test.ts index 917f7f1642e8c..bf1fae4967e95 100644 --- a/src/core/server/integration_tests/saved_objects/routes/import.test.ts +++ b/src/core/server/integration_tests/saved_objects/routes/import.test.ts @@ -13,7 +13,6 @@ import supertest from 'supertest'; import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; import type { ICoreUsageStatsClient } from '@kbn/core-usage-data-base-server-internal'; -import type { Logger, LogLevelId } from '@kbn/logging'; import { coreUsageStatsClientMock, coreUsageDataServiceMock, @@ -28,6 +27,7 @@ import { type InternalSavedObjectsRequestHandlerContext, } from '@kbn/core-saved-objects-server-internal'; import { setupServer, createExportableType } from '@kbn/core-test-helpers-test-utils'; +import { loggerMock, type MockedLogger } from '@kbn/logging-mocks'; type SetupServerReturn = Awaited>; @@ -41,6 +41,7 @@ describe(`POST ${URL}`, () => { let httpSetup: SetupServerReturn['httpSetup']; let handlerContext: SetupServerReturn['handlerContext']; let savedObjectsClient: ReturnType; + let mockLogger: MockedLogger; const emptyResponse = { saved_objects: [], total: 0, per_page: 0, page: 0 }; const mockIndexPattern = { @@ -57,20 +58,10 @@ describe(`POST ${URL}`, () => { references: [], managed: false, }; - const mockLogger: jest.Mocked = { - debug: jest.fn(), - info: jest.fn(), - error: jest.fn(), - warn: jest.fn(), - trace: jest.fn(), - fatal: jest.fn(), - log: jest.fn(), - isLevelEnabled: jest.fn((level: LogLevelId) => true), - get: jest.fn(() => mockLogger), - }; beforeEach(async () => { ({ server, httpSetup, handlerContext } = await setupServer()); + mockLogger = loggerMock.create(); handlerContext.savedObjects.typeRegistry.getImportableAndExportableTypes.mockReturnValue( allowedTypes.map(createExportableType) ); 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 fbe64258a5704..b814538466d73 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 @@ -221,16 +221,13 @@ kibana_vars=( xpack.actions.proxyBypassHosts xpack.actions.proxyHeaders xpack.actions.proxyOnlyHosts - xpack.actions.proxyRejectUnauthorizedCertificates xpack.actions.proxyUrl - xpack.actions.rejectUnauthorized xpack.actions.responseTimeout xpack.actions.ssl.proxyVerificationMode xpack.actions.ssl.verificationMode xpack.alerting.healthCheck.interval xpack.alerting.invalidateApiKeysTask.interval xpack.alerting.invalidateApiKeysTask.removalDelay - xpack.alerting.defaultRuleTaskTimeout xpack.alerting.rules.run.timeout xpack.alerting.rules.run.ruleTypeOverrides xpack.alerting.cancelAlertsOnRuleTimeout @@ -240,9 +237,6 @@ kibana_vars=( xpack.alerting.rules.run.alerts.max xpack.alerting.rules.run.actions.connectorTypeOverrides xpack.alerting.maxScheduledPerMinute - xpack.alerts.healthCheck.interval - xpack.alerts.invalidateApiKeysTask.interval - xpack.alerts.invalidateApiKeysTask.removalDelay xpack.apm.indices.error xpack.apm.indices.metric xpack.apm.indices.onboarding 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 a3925b3a04f24..7a64ada1bfff9 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/run.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/run.ts @@ -51,7 +51,7 @@ export async function runDockerGenerator( */ if (flags.baseImage === 'wolfi') baseImageName = - 'docker.elastic.co/wolfi/chainguard-base:latest@sha256:18153942f0d6e97bc6131cd557c7ed3be6e892846a5df0760896eb8d15b1b236'; + 'docker.elastic.co/wolfi/chainguard-base:latest@sha256:26caa6beaee2bbf739a82e91a35173892dfe888d0a744b9e46cdc19a90d8656f'; let imageFlavor = ''; if (flags.baseImage === 'ubi') imageFlavor += `-ubi`; diff --git a/src/dev/i18n_tools/bin/run_i18n_check.ts b/src/dev/i18n_tools/bin/run_i18n_check.ts index 9d7265f84520f..ff00148ab3012 100644 --- a/src/dev/i18n_tools/bin/run_i18n_check.ts +++ b/src/dev/i18n_tools/bin/run_i18n_check.ts @@ -9,7 +9,6 @@ import { Listr } from 'listr2'; import { run } from '@kbn/dev-cli-runner'; -import { ToolingLog } from '@kbn/tooling-log'; import { getTimeReporter } from '@kbn/ci-stats-reporter'; import { isFailError } from '@kbn/dev-cli-errors'; import { I18nCheckTaskContext, MessageDescriptor } from '../types'; @@ -24,13 +23,7 @@ import { import { TaskReporter } from '../utils/task_reporter'; import { flagFailError, isDefined, undefinedOrBoolean } from '../utils/verify_bin_flags'; -const toolingLog = new ToolingLog({ - level: 'info', - writeTo: process.stdout, -}); - const runStartTime = Date.now(); -const reportTime = getTimeReporter(toolingLog, 'scripts/i18n_check'); const skipOnNoTranslations = ({ config }: I18nCheckTaskContext) => !config?.translations.length && 'No translations found.'; @@ -50,9 +43,13 @@ run( namespace: namespace, fix = false, path, + silent, + quiet, }, log, }) => { + const reportTime = getTimeReporter(log, 'scripts/i18n_check'); + if ( fix && (isDefined(ignoreIncompatible) || @@ -131,13 +128,15 @@ run( { concurrent: false, exitOnError: true, - renderer: process.env.CI ? 'verbose' : ('default' as any), + forceTTY: false, + renderer: + ((silent || quiet) && 'silent') || (process.env.CI ? 'verbose' : ('default' as any)), } ); try { const messages: Map = new Map(); - const taskReporter = new TaskReporter({ toolingLog }); + const taskReporter = new TaskReporter({ toolingLog: log }); await list.run({ messages, taskReporter }); reportTime(runStartTime, 'total', { @@ -150,6 +149,7 @@ run( reportTime(runStartTime, 'error', { success: false, }); + log.error(error); } else { log.error('Unhandled exception!'); log.error(error); diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index 3a45c6394f20f..8609eef92a268 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -87,7 +87,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.3': ['Elastic License 2.0'], - '@elastic/eui@97.2.0': ['Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0'], + '@elastic/eui@97.3.0': ['Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0'], 'language-subtag-registry@0.3.21': ['CC-BY-4.0'], // retired ODC‑By license https://github.com/mattcg/language-subtag-registry 'buffers@0.1.1': ['MIT'], // license in importing module https://www.npmjs.com/package/binary '@bufbuild/protobuf@1.2.1': ['Apache-2.0'], // license (Apache-2.0 AND BSD-3-Clause) diff --git a/src/plugins/dashboard/kibana.jsonc b/src/plugins/dashboard/kibana.jsonc index c84d4a9dc293d..9d47ab95c8872 100644 --- a/src/plugins/dashboard/kibana.jsonc +++ b/src/plugins/dashboard/kibana.jsonc @@ -16,6 +16,7 @@ "dataViews", "dataViewEditor", "embeddable", + "fieldFormats", "controls", "inspector", "navigation", diff --git a/src/plugins/dashboard/public/dashboard_app/hooks/use_observability_ai_assistant_context.tsx b/src/plugins/dashboard/public/dashboard_app/hooks/use_observability_ai_assistant_context.tsx index c20e8fcd1dc76..39ae4594d5bc8 100644 --- a/src/plugins/dashboard/public/dashboard_app/hooks/use_observability_ai_assistant_context.tsx +++ b/src/plugins/dashboard/public/dashboard_app/hooks/use_observability_ai_assistant_context.tsx @@ -114,9 +114,11 @@ export function useObservabilityAIAssistantContext({ }, metric: { type: 'object', + properties: {}, }, gauge: { type: 'object', + properties: {}, }, pie: { type: 'object', @@ -158,6 +160,7 @@ export function useObservabilityAIAssistantContext({ }, table: { type: 'object', + properties: {}, }, tagcloud: { type: 'object', diff --git a/src/plugins/dashboard/public/dashboard_container/state/dashboard_container_reducers.ts b/src/plugins/dashboard/public/dashboard_container/state/dashboard_container_reducers.ts index 0bb33a05c36ce..c0c39b0ffd284 100644 --- a/src/plugins/dashboard/public/dashboard_container/state/dashboard_container_reducers.ts +++ b/src/plugins/dashboard/public/dashboard_container/state/dashboard_container_reducers.ts @@ -9,6 +9,7 @@ import { PayloadAction } from '@reduxjs/toolkit'; +import { isFilterPinned } from '@kbn/es-query'; import { DashboardReduxState, DashboardStateFromSaveModal, @@ -94,13 +95,20 @@ export const dashboardContainerReducers = { * `timeRestore` is `false`, this causes unecessary data fetches for the control group. * 2) The view mode, since resetting should never impact this - sometimes the Dashboard saved objects * have this saved in and we don't want resetting to cause unexpected view mode changes. + * 3) Pinned filters. */ resetToLastSavedInput: ( state: DashboardReduxState, action: PayloadAction ) => { + const keepPinnedFilters = [ + ...state.explicitInput.filters.filter(isFilterPinned), + ...action.payload.filters, + ]; + state.explicitInput = { ...action.payload, + filters: keepPinnedFilters, ...(!state.explicitInput.timeRestore && { timeRange: state.explicitInput.timeRange }), viewMode: state.explicitInput.viewMode, }; diff --git a/src/plugins/data/server/search/session/session_service.ts b/src/plugins/data/server/search/session/session_service.ts index 4ef741ec78387..48b563c5585ca 100644 --- a/src/plugins/data/server/search/session/session_service.ts +++ b/src/plugins/data/server/search/session/session_service.ts @@ -402,8 +402,8 @@ export class SearchSessionService implements ISearchSessionService { const session = await this.get(deps, user, sessionId); const requestHash = createRequestHash(searchRequest.params); if (!Object.hasOwn(session.attributes.idMapping, requestHash)) { - this.logger.error(`SearchSessionService: getId | ${sessionId} | ${requestHash} not found`); - this.logger.debug( + this.logger.debug(`SearchSessionService: getId | ${sessionId} | ${requestHash} not found`); + this.logger.error( `SearchSessionService: getId not found search with params: ${JSON.stringify( searchRequest.params )}` diff --git a/src/plugins/data_views/public/data_views/data_views_api_client.test.ts b/src/plugins/data_views/public/data_views/data_views_api_client.test.ts index 8e1261802fbbc..4eaf2e88f56d9 100644 --- a/src/plugins/data_views/public/data_views/data_views_api_client.test.ts +++ b/src/plugins/data_views/public/data_views/data_views_api_client.test.ts @@ -30,9 +30,6 @@ describe('IndexPatternsApiClient', () => { expect(fetchSpy).toHaveBeenCalledWith(expectedPath, { // not sure what asResponse is but the rest of the results are useful asResponse: true, - headers: { - 'user-hash': '', - }, query: { allow_hidden: undefined, allow_no_index: undefined, diff --git a/src/plugins/data_views/public/data_views/data_views_api_client.ts b/src/plugins/data_views/public/data_views/data_views_api_client.ts index e569e7f25bff6..233b05ea7bc22 100644 --- a/src/plugins/data_views/public/data_views/data_views_api_client.ts +++ b/src/plugins/data_views/public/data_views/data_views_api_client.ts @@ -56,6 +56,7 @@ export class DataViewsApiClient implements IDataViewsApiClient { const userId = await this.getCurrentUserId(); const userHash = userId ? await sha1(userId) : ''; + const headers = userHash ? { 'user-hash': userHash } : undefined; const request = body ? this.http.post(url, { query, body, version, asResponse }) @@ -64,7 +65,7 @@ export class DataViewsApiClient implements IDataViewsApiClient { version, ...cacheOptions, asResponse, - headers: { 'user-hash': userHash }, + headers, }); return request.catch((resp) => { diff --git a/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/convert_to_top_nav_item.test.ts b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/convert_to_top_nav_item.test.ts new file mode 100644 index 0000000000000..2fb65563cddfb --- /dev/null +++ b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/convert_to_top_nav_item.test.ts @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { + AppMenuActionPrimary, + AppMenuActionSecondary, + AppMenuActionSubmenuCustom, + AppMenuActionType, +} from '@kbn/discover-utils'; +import { convertAppMenuItemToTopNavItem } from './convert_to_top_nav_item'; +import { discoverServiceMock } from '../../../../../__mocks__/services'; + +describe('convertAppMenuItemToTopNavItem', () => { + it('should convert a primary AppMenuItem to TopNavMenuData', () => { + const appMenuItem: AppMenuActionPrimary = { + id: 'action-1', + type: AppMenuActionType.primary, + controlProps: { + label: 'Action 1', + testId: 'action-1', + iconType: 'share', + onClick: jest.fn(), + }, + }; + + const topNavItem = convertAppMenuItemToTopNavItem({ + appMenuItem, + services: discoverServiceMock, + }); + + expect(topNavItem).toEqual({ + id: 'action-1', + label: 'Action 1', + description: 'Action 1', + testId: 'action-1', + run: expect.any(Function), + iconType: 'share', + iconOnly: true, + }); + }); + + it('should convert a secondary AppMenuItem to TopNavMenuData', () => { + const appMenuItem: AppMenuActionSecondary = { + id: 'action-2', + type: AppMenuActionType.secondary, + controlProps: { + label: 'Action Secondary', + testId: 'action-secondary', + onClick: jest.fn(), + }, + }; + + const topNavItem = convertAppMenuItemToTopNavItem({ + appMenuItem, + services: discoverServiceMock, + }); + + expect(topNavItem).toEqual({ + id: 'action-2', + label: 'Action Secondary', + description: 'Action Secondary', + testId: 'action-secondary', + run: expect.any(Function), + }); + }); + + it('should convert a custom AppMenuItem to TopNavMenuData', () => { + const appMenuItem: AppMenuActionSubmenuCustom = { + id: 'action-3', + type: AppMenuActionType.custom, + label: 'Action submenu', + testId: 'action-submenu', + actions: [ + { + id: 'action-3-1', + type: AppMenuActionType.custom, + controlProps: { + label: 'Action 3.1', + testId: 'action-3-1', + onClick: jest.fn(), + }, + }, + { + id: 'action-3-2', + type: AppMenuActionType.submenuHorizontalRule, + }, + { + id: 'action-3-3', + type: AppMenuActionType.custom, + controlProps: { + label: 'Action 3.3', + testId: 'action-3-3', + onClick: jest.fn(), + }, + }, + ], + }; + + const topNavItem = convertAppMenuItemToTopNavItem({ + appMenuItem, + services: discoverServiceMock, + }); + + expect(topNavItem).toEqual({ + id: 'action-3', + label: 'Action submenu', + description: 'Action submenu', + testId: 'action-submenu', + run: expect.any(Function), + }); + }); +}); diff --git a/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/convert_to_top_nav_item.ts b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/convert_to_top_nav_item.ts new file mode 100644 index 0000000000000..2ff2d531d77cf --- /dev/null +++ b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/convert_to_top_nav_item.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { AppMenuActionType, AppMenuItem } from '@kbn/discover-utils'; +import type { TopNavMenuData } from '@kbn/navigation-plugin/public'; +import { runAppMenuAction, runAppMenuPopoverAction } from './run_app_menu_action'; +import { DiscoverServices } from '../../../../../build_services'; + +export function convertAppMenuItemToTopNavItem({ + appMenuItem, + services, +}: { + appMenuItem: AppMenuItem; + services: DiscoverServices; +}): TopNavMenuData { + if ('actions' in appMenuItem) { + return { + id: appMenuItem.id, + label: appMenuItem.label, + description: appMenuItem.description ?? appMenuItem.label, + testId: appMenuItem.testId, + run: (anchorElement: HTMLElement) => { + runAppMenuPopoverAction({ + appMenuItem, + anchorElement, + services, + }); + }, + }; + } + + return { + id: appMenuItem.id, + label: appMenuItem.controlProps.label, + description: appMenuItem.controlProps.description ?? appMenuItem.controlProps.label, + testId: appMenuItem.controlProps.testId, + run: async (anchorElement: HTMLElement) => { + await runAppMenuAction({ + appMenuItem, + anchorElement, + services, + }); + }, + ...(appMenuItem.type === AppMenuActionType.primary + ? { iconType: appMenuItem.controlProps.iconType, iconOnly: true } + : {}), + }; +} diff --git a/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.test.tsx b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_alerts.test.tsx similarity index 74% rename from src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.test.tsx rename to src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_alerts.test.tsx index fb9f127b83d86..a658ca750cf28 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.test.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_alerts.test.tsx @@ -10,28 +10,40 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { findTestSubject } from '@elastic/eui/lib/test'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { AlertsPopover } from './open_alerts_popover'; -import { discoverServiceMock } from '../../../../__mocks__/services'; -import { dataViewWithTimefieldMock } from '../../../../__mocks__/data_view_with_timefield'; -import { dataViewWithNoTimefieldMock } from '../../../../__mocks__/data_view_no_timefield'; import { dataViewMock } from '@kbn/discover-utils/src/__mocks__'; -import { getDiscoverStateMock } from '../../../../__mocks__/discover_state.mock'; +import { AppMenuActionsMenuPopover } from './run_app_menu_action'; +import { getAlertsAppMenuItem } from './get_alerts'; +import { discoverServiceMock } from '../../../../../__mocks__/services'; +import { dataViewWithTimefieldMock } from '../../../../../__mocks__/data_view_with_timefield'; +import { dataViewWithNoTimefieldMock } from '../../../../../__mocks__/data_view_no_timefield'; +import { getDiscoverStateMock } from '../../../../../__mocks__/discover_state.mock'; const mount = (dataView = dataViewMock, isEsqlMode = false) => { const stateContainer = getDiscoverStateMock({ isTimeBased: true }); stateContainer.actions.setDataView(dataView); + + const discoverParamsMock = { + dataView, + adHocDataViews: [], + isEsqlMode, + onNewSearch: jest.fn(), + onOpenSavedSearch: jest.fn(), + onUpdateAdHocDataViews: jest.fn(), + }; + + const alertsAppMenuItem = getAlertsAppMenuItem({ + discoverParams: discoverParamsMock, + services: discoverServiceMock, + stateContainer, + }); + return mountWithIntl( - - - + ); }; diff --git a/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_alerts.tsx b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_alerts.tsx new file mode 100644 index 0000000000000..d6d8bb81bac09 --- /dev/null +++ b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_alerts.tsx @@ -0,0 +1,179 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React, { useCallback, useMemo } from 'react'; +import type { DataView } from '@kbn/data-plugin/common'; +import { i18n } from '@kbn/i18n'; +import { + AppMenuActionId, + AppMenuActionSubmenuSecondary, + AppMenuActionType, +} from '@kbn/discover-utils'; +import { + AlertConsumers, + ES_QUERY_ID, + RuleCreationValidConsumer, + STACK_ALERTS_FEATURE_ID, +} from '@kbn/rule-data-utils'; +import { RuleTypeMetaData } from '@kbn/alerting-plugin/common'; +import { DiscoverStateContainer } from '../../../state_management/discover_state'; +import { AppMenuDiscoverParams } from './types'; +import { DiscoverServices } from '../../../../../build_services'; + +const EsQueryValidConsumer: RuleCreationValidConsumer[] = [ + AlertConsumers.INFRASTRUCTURE, + AlertConsumers.LOGS, + AlertConsumers.OBSERVABILITY, + STACK_ALERTS_FEATURE_ID, +]; + +interface EsQueryAlertMetaData extends RuleTypeMetaData { + isManagementPage?: boolean; + adHocDataViewList: DataView[]; +} + +const CreateAlertFlyout: React.FC<{ + discoverParams: AppMenuDiscoverParams; + services: DiscoverServices; + onFinishAction: () => void; + stateContainer: DiscoverStateContainer; +}> = ({ stateContainer, discoverParams, services, onFinishAction }) => { + const query = stateContainer.appState.getState().query; + + const { dataView, isEsqlMode, adHocDataViews, onUpdateAdHocDataViews } = discoverParams; + const { triggersActionsUi } = services; + const timeField = getTimeField(dataView); + + /** + * Provides the default parameters used to initialize the new rule + */ + const getParams = useCallback(() => { + if (isEsqlMode) { + return { + searchType: 'esqlQuery', + esqlQuery: query, + timeField, + }; + } + const savedQueryId = stateContainer.appState.getState().savedQuery; + return { + searchType: 'searchSource', + searchConfiguration: stateContainer.savedSearchState + .getState() + .searchSource.getSerializedFields(), + savedQueryId, + }; + }, [isEsqlMode, stateContainer.appState, stateContainer.savedSearchState, query, timeField]); + + const discoverMetadata: EsQueryAlertMetaData = useMemo( + () => ({ + isManagementPage: false, + adHocDataViewList: adHocDataViews, + }), + [adHocDataViews] + ); + + return triggersActionsUi?.getAddRuleFlyout({ + metadata: discoverMetadata, + consumer: 'alerts', + onClose: (_, metadata) => { + onUpdateAdHocDataViews(metadata!.adHocDataViewList); + onFinishAction(); + }, + onSave: async (metadata) => { + onUpdateAdHocDataViews(metadata!.adHocDataViewList); + }, + canChangeTrigger: false, + ruleTypeId: ES_QUERY_ID, + initialValues: { params: getParams() }, + validConsumers: EsQueryValidConsumer, + useRuleProducer: true, + // Default to the Logs consumer if it's available. This should fall back to Stack Alerts if it's not. + initialSelectedConsumer: AlertConsumers.LOGS, + }); +}; + +export const getAlertsAppMenuItem = ({ + discoverParams, + services, + stateContainer, +}: { + discoverParams: AppMenuDiscoverParams; + services: DiscoverServices; + stateContainer: DiscoverStateContainer; +}): AppMenuActionSubmenuSecondary => { + const { dataView, isEsqlMode } = discoverParams; + const timeField = getTimeField(dataView); + const hasTimeFieldName = !isEsqlMode ? Boolean(dataView?.timeFieldName) : Boolean(timeField); + + return { + id: AppMenuActionId.alerts, + type: AppMenuActionType.secondary, + label: i18n.translate('discover.localMenu.localMenu.alertsTitle', { + defaultMessage: 'Alerts', + }), + description: i18n.translate('discover.localMenu.alertsDescription', { + defaultMessage: 'Alerts', + }), + testId: 'discoverAlertsButton', + actions: [ + { + id: AppMenuActionId.createRule, + type: AppMenuActionType.secondary, + controlProps: { + label: i18n.translate('discover.alerts.createSearchThreshold', { + defaultMessage: 'Create search threshold rule', + }), + iconType: 'bell', + testId: 'discoverCreateAlertButton', + disableButton: !hasTimeFieldName, + tooltip: hasTimeFieldName + ? undefined + : i18n.translate('discover.alerts.missedTimeFieldToolTip', { + defaultMessage: 'Data view does not have a time field.', + }), + onClick: async (params) => { + return ( + + ); + }, + }, + }, + { + id: 'alertsDivider', + type: AppMenuActionType.submenuHorizontalRule, + }, + { + id: AppMenuActionId.manageRulesAndConnectors, + type: AppMenuActionType.secondary, + controlProps: { + label: i18n.translate('discover.alerts.manageRulesAndConnectors', { + defaultMessage: 'Manage rules and connectors', + }), + iconType: 'tableOfContents', + testId: 'discoverManageAlertsButton', + href: services.application.getUrlForApp( + 'management/insightsAndAlerting/triggersActions/rules' + ), + onClick: undefined, + }, + }, + ], + }; +}; + +function getTimeField(dataView: DataView | undefined) { + const dateFields = dataView?.fields.getByType('date'); + return dataView?.timeFieldName || dateFields?.[0]?.name; +} diff --git a/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_inspect.tsx b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_inspect.tsx new file mode 100644 index 0000000000000..5943f598c9aef --- /dev/null +++ b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_inspect.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { AppMenuActionId, AppMenuActionType, AppMenuActionSecondary } from '@kbn/discover-utils'; +import { i18n } from '@kbn/i18n'; + +export const getInspectAppMenuItem = ({ + onOpenInspector, +}: { + onOpenInspector: () => void; +}): AppMenuActionSecondary => { + return { + id: AppMenuActionId.inspect, + type: AppMenuActionType.secondary, + controlProps: { + label: i18n.translate('discover.localMenu.inspectTitle', { + defaultMessage: 'Inspect', + }), + description: i18n.translate('discover.localMenu.openInspectorForSearchDescription', { + defaultMessage: 'Open Inspector for search', + }), + testId: 'openInspectorButton', + onClick: () => { + onOpenInspector(); + }, + }, + }; +}; diff --git a/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_new_search.tsx b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_new_search.tsx new file mode 100644 index 0000000000000..b67f14f31c56a --- /dev/null +++ b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_new_search.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { AppMenuActionId, AppMenuActionType, AppMenuActionPrimary } from '@kbn/discover-utils'; +import { i18n } from '@kbn/i18n'; + +export const getNewSearchAppMenuItem = ({ + onNewSearch, +}: { + onNewSearch: () => void; +}): AppMenuActionPrimary => { + return { + id: AppMenuActionId.new, + type: AppMenuActionType.primary, + controlProps: { + label: i18n.translate('discover.localMenu.localMenu.newSearchTitle', { + defaultMessage: 'New', + }), + description: i18n.translate('discover.localMenu.newSearchDescription', { + defaultMessage: 'New Search', + }), + iconType: 'plus', + testId: 'discoverNewButton', + onClick: () => { + onNewSearch(); + }, + }, + }; +}; diff --git a/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_open_search.tsx b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_open_search.tsx new file mode 100644 index 0000000000000..e8f6c5448d602 --- /dev/null +++ b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_open_search.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React from 'react'; +import { AppMenuActionId, AppMenuActionType, AppMenuActionPrimary } from '@kbn/discover-utils'; +import { i18n } from '@kbn/i18n'; +import { OpenSearchPanel } from '../open_search_panel'; + +export const getOpenSearchAppMenuItem = ({ + onOpenSavedSearch, +}: { + onOpenSavedSearch: (savedSearchId: string) => void; +}): AppMenuActionPrimary => { + return { + id: AppMenuActionId.open, + type: AppMenuActionType.primary, + controlProps: { + label: i18n.translate('discover.localMenu.openTitle', { + defaultMessage: 'Open', + }), + description: i18n.translate('discover.localMenu.openSavedSearchDescription', { + defaultMessage: 'Open Saved Search', + }), + iconType: 'folderOpen', + testId: 'discoverOpenButton', + onClick: ({ onFinishAction }) => { + return ; + }, + }, + }; +}; diff --git a/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_share.tsx b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_share.tsx new file mode 100644 index 0000000000000..f1a030a40ea0a --- /dev/null +++ b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/get_share.tsx @@ -0,0 +1,135 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { AppMenuActionPrimary, AppMenuActionId, AppMenuActionType } from '@kbn/discover-utils'; +import { omit } from 'lodash'; +import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public'; +import { i18n } from '@kbn/i18n'; +import { DiscoverStateContainer } from '../../../state_management/discover_state'; +import { getSharingData, showPublicUrlSwitch } from '../../../../../utils/get_sharing_data'; +import { DiscoverAppLocatorParams } from '../../../../../../common/app_locator'; +import { AppMenuDiscoverParams } from './types'; +import { DiscoverServices } from '../../../../../build_services'; + +export const getShareAppMenuItem = ({ + discoverParams, + services, + stateContainer, +}: { + discoverParams: AppMenuDiscoverParams; + services: DiscoverServices; + stateContainer: DiscoverStateContainer; +}): AppMenuActionPrimary => { + return { + id: AppMenuActionId.share, + type: AppMenuActionType.primary, + controlProps: { + label: i18n.translate('discover.localMenu.shareTitle', { + defaultMessage: 'Share', + }), + description: i18n.translate('discover.localMenu.shareSearchDescription', { + defaultMessage: 'Share Search', + }), + iconType: 'share', + testId: 'shareTopNavButton', + onClick: async ({ anchorElement }) => { + const { dataView, isEsqlMode } = discoverParams; + + if (!services.share) { + return; + } + + const savedSearch = stateContainer.savedSearchState.getState(); + const searchSourceSharingData = await getSharingData( + savedSearch.searchSource, + stateContainer.appState.getState(), + services, + isEsqlMode + ); + + const { locator, notifications } = services; + const appState = stateContainer.appState.getState(); + const { timefilter } = services.data.query.timefilter; + const timeRange = timefilter.getTime(); + const refreshInterval = timefilter.getRefreshInterval(); + const filters = services.filterManager.getFilters(); + + // Share -> Get links -> Snapshot + const params: DiscoverAppLocatorParams = { + ...omit(appState, 'dataSource'), + ...(savedSearch.id ? { savedSearchId: savedSearch.id } : {}), + ...(dataView?.isPersisted() + ? { dataViewId: dataView?.id } + : { dataViewSpec: dataView?.toMinimalSpec() }), + filters, + timeRange, + refreshInterval, + }; + const relativeUrl = locator.getRedirectUrl(params); + + // This logic is duplicated from `relativeToAbsolute` (for bundle size reasons). Ultimately, this should be + // replaced when https://github.com/elastic/kibana/issues/153323 is implemented. + const link = document.createElement('a'); + link.setAttribute('href', relativeUrl); + const shareableUrl = link.href; + + // Share -> Get links -> Saved object + let shareableUrlForSavedObject = await locator.getUrl( + { savedSearchId: savedSearch.id }, + { absolute: true } + ); + + // UrlPanelContent forces a '_g' parameter in the saved object URL: + // https://github.com/elastic/kibana/blob/a30508153c1467b1968fb94faf1debc5407f61ea/src/plugins/share/public/components/url_panel_content.tsx#L230 + // Since our locator doesn't add the '_g' parameter if it's not needed, UrlPanelContent + // will interpret it as undefined and add '?_g=' to the URL, which is invalid in Discover, + // so instead we add an empty object for the '_g' parameter to the URL. + shareableUrlForSavedObject = setStateToKbnUrl( + '_g', + {}, + undefined, + shareableUrlForSavedObject + ); + + services.share.toggleShareContextMenu({ + anchorElement, + allowEmbed: false, + allowShortUrl: !!services.capabilities.discover.createShortUrl, + shareableUrl, + shareableUrlForSavedObject, + shareableUrlLocatorParams: { locator, params }, + objectId: savedSearch.id, + objectType: 'search', + objectTypeMeta: { + title: i18n.translate('discover.share.shareModal.title', { + defaultMessage: 'Share this search', + }), + }, + sharingData: { + isTextBased: isEsqlMode, + locatorParams: [{ id: locator.id, params }], + ...searchSourceSharingData, + // CSV reports can be generated without a saved search so we provide a fallback title + title: + savedSearch.title || + i18n.translate('discover.localMenu.fallbackReportTitle', { + defaultMessage: 'Untitled discover search', + }), + }, + isDirty: !savedSearch.id || stateContainer.appState.hasChanged(), + showPublicUrlSwitch, + onClose: () => { + anchorElement?.focus(); + }, + toasts: notifications.toasts, + }); + }, + }, + }; +}; diff --git a/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/index.ts b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/index.ts new file mode 100644 index 0000000000000..6a5c2f31946a2 --- /dev/null +++ b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export { getAlertsAppMenuItem } from './get_alerts'; +export { getNewSearchAppMenuItem } from './get_new_search'; +export { getOpenSearchAppMenuItem } from './get_open_search'; +export { getShareAppMenuItem } from './get_share'; +export { getInspectAppMenuItem } from './get_inspect'; +export { convertAppMenuItemToTopNavItem } from './convert_to_top_nav_item'; +export * from './types'; diff --git a/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/run_app_menu_action.test.tsx b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/run_app_menu_action.test.tsx new file mode 100644 index 0000000000000..952063317d91c --- /dev/null +++ b/src/plugins/discover/public/application/main/components/top_nav/app_menu_actions/run_app_menu_action.test.tsx @@ -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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React from 'react'; +import { screen } from '@testing-library/react'; +import { AppMenuActionSubmenuCustom, AppMenuActionType, AppMenuItem } from '@kbn/discover-utils'; +import { discoverServiceMock } from '../../../../../__mocks__/services'; +import { runAppMenuAction, runAppMenuPopoverAction } from './run_app_menu_action'; + +describe('run app menu actions', () => { + describe('runAppMenuAction', () => { + it('should call the action correctly', () => { + const appMenuItem: AppMenuItem = { + id: 'action-1', + type: AppMenuActionType.primary, + controlProps: { + label: 'Action 1', + testId: 'action-1', + iconType: 'share', + onClick: jest.fn(), + }, + }; + + const anchorElement = document.createElement('div'); + + runAppMenuAction({ + appMenuItem, + anchorElement, + services: discoverServiceMock, + }); + + expect(appMenuItem.controlProps.onClick).toHaveBeenCalled(); + }); + + it('should call the action and render a custom content', async () => { + const appMenuItem: AppMenuItem = { + id: 'action-1', + type: AppMenuActionType.primary, + controlProps: { + label: 'Action 1', + testId: 'action-1', + iconType: 'share', + onClick: jest.fn(({ onFinishAction }) => ( +
); diff --git a/test/api_integration/apis/home/sample_data.ts b/test/api_integration/apis/home/sample_data.ts index d290f772fdec5..13ab83e85a05a 100644 --- a/test/api_integration/apis/home/sample_data.ts +++ b/test/api_integration/apis/home/sample_data.ts @@ -24,12 +24,6 @@ export default function ({ getService }: FtrProviderContext) { * @see {@link src/plugins/home/server/services/sample_data/data_sets/flights/index.ts} */ const FLIGHTS_OVERVIEW_DASHBOARD_ID = '7adfa750-4c81-11e8-b3d7-01146121b73d'; - const FLIGHTS_CANVAS_APPLINK_PATH = - '/app/canvas#/workpad/workpad-a474e74b-aedc-47c3-894a-db77e62c41e0'; // includes default ID of the flights canvas applink path - - const includesPathInAppLinks = (appLinks: Array<{ path: string }>, path: string): boolean => { - return appLinks.some((item) => item.path === path); - }; describe('sample data apis', () => { before(async () => { @@ -52,12 +46,7 @@ export default function ({ getService }: FtrProviderContext) { const flightsData = findFlightsData(resp); expect(flightsData.status).to.be('not_installed'); - // Check and make sure the sample dataset reflects the default object IDs, because no sample data objects exist. - // Instead of checking each object ID, we check the dashboard and canvas app link as representatives. expect(flightsData.overviewDashboard).to.be(FLIGHTS_OVERVIEW_DASHBOARD_ID); - expect(includesPathInAppLinks(flightsData.appLinks, FLIGHTS_CANVAS_APPLINK_PATH)).to.be( - true - ); }); }); @@ -79,7 +68,7 @@ export default function ({ getService }: FtrProviderContext) { expect(resp.body).to.eql({ elasticsearchIndicesCreated: { kibana_sample_data_flights: 13014 }, - kibanaSavedObjectsLoaded: 8, + kibanaSavedObjectsLoaded: 7, }); }); @@ -136,19 +125,11 @@ export default function ({ getService }: FtrProviderContext) { const flightsData = findFlightsData(resp); expect(flightsData.status).to.be('installed'); - // Check and make sure the sample dataset reflects the existing object IDs in each space. - // Instead of checking each object ID, we check the dashboard and canvas app link as representatives. if (space === 'default') { expect(flightsData.overviewDashboard).to.be(FLIGHTS_OVERVIEW_DASHBOARD_ID); - expect(includesPathInAppLinks(flightsData.appLinks, FLIGHTS_CANVAS_APPLINK_PATH)).to.be( - true - ); } else { // the sample data objects installed in the 'other' space had their IDs regenerated upon import expect(flightsData.overviewDashboard).not.to.be(FLIGHTS_OVERVIEW_DASHBOARD_ID); - expect(includesPathInAppLinks(flightsData.appLinks, FLIGHTS_CANVAS_APPLINK_PATH)).to.be( - false - ); } }); }); @@ -186,12 +167,7 @@ export default function ({ getService }: FtrProviderContext) { const flightsData = findFlightsData(resp); expect(flightsData.status).to.be('not_installed'); - // Check and make sure the sample dataset reflects the default object IDs, because no sample data objects exist. - // Instead of checking each object ID, we check the dashboard and canvas app link as representatives. expect(flightsData.overviewDashboard).to.be(FLIGHTS_OVERVIEW_DASHBOARD_ID); - expect(includesPathInAppLinks(flightsData.appLinks, FLIGHTS_CANVAS_APPLINK_PATH)).to.be( - true - ); }); }); } diff --git a/test/examples/discover_customization_examples/customizations.ts b/test/examples/discover_customization_examples/customizations.ts index f9e29611dc0cc..38e8e8ab2a6c5 100644 --- a/test/examples/discover_customization_examples/customizations.ts +++ b/test/examples/discover_customization_examples/customizations.ts @@ -48,15 +48,9 @@ export default ({ getService, getPageObjects }: FtrProviderContext) => { }); it('Top nav', async () => { - await testSubjects.existOrFail('customOptionsButton'); await testSubjects.existOrFail('shareTopNavButton'); - await testSubjects.existOrFail('documentExplorerButton'); await testSubjects.missingOrFail('discoverNewButton'); await testSubjects.missingOrFail('discoverOpenButton'); - await testSubjects.click('customOptionsButton'); - await testSubjects.existOrFail('customOptionsPopover'); - await testSubjects.click('customOptionsButton'); - await testSubjects.missingOrFail('customOptionsPopover'); }); it('Search bar', async () => { diff --git a/test/functional/apps/console/_misc_console_behavior.ts b/test/functional/apps/console/_misc_console_behavior.ts index fc53b6b37fb51..4185a2198fa32 100644 --- a/test/functional/apps/console/_misc_console_behavior.ts +++ b/test/functional/apps/console/_misc_console_behavior.ts @@ -154,8 +154,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.console.openConsole(); }); - // Failing: See https://github.com/elastic/kibana/issues/193868 - describe.skip('customizable font size', () => { + describe('customizable font size', () => { it('should allow the font size to be customized', async () => { await PageObjects.console.openConfig(); await PageObjects.console.setFontSizeSetting(20); diff --git a/test/functional/apps/discover/context_awareness/extensions/_get_app_menu.ts b/test/functional/apps/discover/context_awareness/extensions/_get_app_menu.ts new file mode 100644 index 0000000000000..9b019a67d6507 --- /dev/null +++ b/test/functional/apps/discover/context_awareness/extensions/_get_app_menu.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import kbnRison from '@kbn/rison'; +import type { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const { common, discover, header } = getPageObjects([ + 'common', + 'timePicker', + 'discover', + 'header', + ]); + const esArchiver = getService('esArchiver'); + const testSubjects = getService('testSubjects'); + + describe('extension getAppMenu', () => { + before(async () => { + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + }); + + after(async () => { + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + }); + + it('should render the main actions and the action from root profile', async () => { + const state = kbnRison.encode({ + dataSource: { type: 'esql' }, + query: { esql: 'from logstash* | sort @timestamp desc' }, + }); + await common.navigateToActualUrl('discover', `?_a=${state}`, { + ensureCurrentUrl: false, + }); + await header.waitUntilLoadingHasFinished(); + await discover.waitUntilSearchingHasFinished(); + await testSubjects.existOrFail('discoverNewButton'); + await testSubjects.existOrFail('discoverAlertsButton'); + await testSubjects.existOrFail('example-custom-root-submenu'); + }); + + it('should render custom actions', async () => { + const state = kbnRison.encode({ + dataSource: { type: 'esql' }, + query: { esql: 'from my-example-logs | sort @timestamp desc' }, + }); + await common.navigateToActualUrl('discover', `?_a=${state}`, { + ensureCurrentUrl: false, + }); + await header.waitUntilLoadingHasFinished(); + await discover.waitUntilSearchingHasFinished(); + await testSubjects.existOrFail('discoverNewButton'); + await testSubjects.existOrFail('discoverAlertsButton'); + await testSubjects.existOrFail('example-custom-root-submenu'); + await testSubjects.existOrFail('example-custom-action'); + + await testSubjects.click('example-custom-root-submenu'); + await testSubjects.existOrFail('example-custom-root-action12'); + + await testSubjects.click('example-custom-root-action12'); + await testSubjects.existOrFail('example-custom-root-action12-flyout'); + await testSubjects.click('euiFlyoutCloseButton'); + + await testSubjects.click('discoverAlertsButton'); + await testSubjects.existOrFail('example-custom-action-under-alerts'); + }); + }); +} diff --git a/test/functional/apps/discover/context_awareness/index.ts b/test/functional/apps/discover/context_awareness/index.ts index f937f38c741f9..40f2df358a4ce 100644 --- a/test/functional/apps/discover/context_awareness/index.ts +++ b/test/functional/apps/discover/context_awareness/index.ts @@ -45,5 +45,6 @@ export default function ({ getService, getPageObjects, loadTestFile }: FtrProvid loadTestFile(require.resolve('./extensions/_get_cell_renderers')); loadTestFile(require.resolve('./extensions/_get_default_app_state')); loadTestFile(require.resolve('./extensions/_get_additional_cell_actions')); + loadTestFile(require.resolve('./extensions/_get_app_menu')); }); } diff --git a/test/functional/apps/discover/group6/_sidebar_field_stats.ts b/test/functional/apps/discover/group6/_sidebar_field_stats.ts index 3cfa2c1da20af..325adb313ed6c 100644 --- a/test/functional/apps/discover/group6/_sidebar_field_stats.ts +++ b/test/functional/apps/discover/group6/_sidebar_field_stats.ts @@ -148,7 +148,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await discover.selectTextBaseLang(); - const testQuery = `from logstash-* [METADATA _index, _id] | sort @timestamp desc | limit 500`; + const testQuery = `from logstash-* METADATA _index, _id | sort @timestamp desc | limit 500`; await monacoEditor.setCodeEditorValue(testQuery); await testSubjects.click('querySubmitButton'); await header.waitUntilLoadingHasFinished(); @@ -168,7 +168,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await unifiedFieldList.clickFieldListPlusFilter('bytes', '0'); const editorValue = await monacoEditor.getCodeEditorValue(); expect(editorValue).to.eql( - `from logstash-* [METADATA _index, _id] | sort @timestamp desc | limit 500\n| WHERE \`bytes\`==0` + `from logstash-* METADATA _index, _id | sort @timestamp desc | limit 500\n| WHERE \`bytes\`==0` ); await unifiedFieldList.closeFieldPopover(); }); @@ -188,7 +188,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await unifiedFieldList.clickFieldListPlusFilter('extension.raw', 'css'); const editorValue = await monacoEditor.getCodeEditorValue(); expect(editorValue).to.eql( - `from logstash-* [METADATA _index, _id] | sort @timestamp desc | limit 500\n| WHERE \`extension.raw\`=="css"` + `from logstash-* METADATA _index, _id | sort @timestamp desc | limit 500\n| WHERE \`extension.raw\`=="css"` ); await unifiedFieldList.closeFieldPopover(); @@ -209,7 +209,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await unifiedFieldList.clickFieldListPlusFilter('clientip', '216.126.255.31'); const editorValue = await monacoEditor.getCodeEditorValue(); expect(editorValue).to.eql( - `from logstash-* [METADATA _index, _id] | sort @timestamp desc | limit 500\n| WHERE \`clientip\`::string=="216.126.255.31"` + `from logstash-* METADATA _index, _id | sort @timestamp desc | limit 500\n| WHERE \`clientip\`::string=="216.126.255.31"` ); await unifiedFieldList.closeFieldPopover(); @@ -234,7 +234,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await unifiedFieldList.clickFieldListExistsFilter('@timestamp'); const editorValue = await monacoEditor.getCodeEditorValue(); expect(editorValue).to.eql( - `from logstash-* [METADATA _index, _id] | sort @timestamp desc | limit 500\n| WHERE \`@timestamp\` is not null` + `from logstash-* METADATA _index, _id | sort @timestamp desc | limit 500\n| WHERE \`@timestamp\` is not null` ); await testSubjects.missingOrFail('dscFieldStats-statsFooter'); await unifiedFieldList.closeFieldPopover(); @@ -269,7 +269,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await unifiedFieldList.clickFieldListPlusFilter('extension', 'css'); const editorValue = await monacoEditor.getCodeEditorValue(); expect(editorValue).to.eql( - `from logstash-* [METADATA _index, _id] | sort @timestamp desc | limit 500\n| WHERE \`extension\`=="css"` + `from logstash-* METADATA _index, _id | sort @timestamp desc | limit 500\n| WHERE \`extension\`=="css"` ); await unifiedFieldList.closeFieldPopover(); diff --git a/test/functional/page_objects/console_page.ts b/test/functional/page_objects/console_page.ts index 29b88787e7ec2..71a4d05aecdb0 100644 --- a/test/functional/page_objects/console_page.ts +++ b/test/functional/page_objects/console_page.ts @@ -9,6 +9,7 @@ import { Key } from 'selenium-webdriver'; import { asyncForEach } from '@kbn/std'; +import expect from '@kbn/expect'; import { FtrService } from '../ftr_provider_context'; export class ConsolePageObject extends FtrService { @@ -368,10 +369,12 @@ export class ConsolePageObject extends FtrService { public async setFontSizeSetting(newSize: number) { // while the settings form opens/loads this may fail, so retry for a while await this.retry.try(async () => { + const newSizeString = String(newSize); const fontSizeInput = await this.testSubjects.find('setting-font-size-input'); await fontSizeInput.clearValue({ withJS: true }); await fontSizeInput.click(); - await fontSizeInput.type(String(newSize)); + await fontSizeInput.type(newSizeString); + expect(await fontSizeInput.getAttribute('value')).to.be(newSizeString); }); } diff --git a/test/functional/page_objects/discover_page.ts b/test/functional/page_objects/discover_page.ts index ab6356075fd81..979e4341931ab 100644 --- a/test/functional/page_objects/discover_page.ts +++ b/test/functional/page_objects/discover_page.ts @@ -164,6 +164,7 @@ export class DiscoverPageObject extends FtrService { public async clickNewSearchButton() { await this.testSubjects.click('discoverNewButton'); + await this.testSubjects.moveMouseTo('unifiedFieldListSidebar__toggle-collapse'); // cancel tooltips await this.header.waitUntilLoadingHasFinished(); } diff --git a/test/functional/page_objects/home_page.ts b/test/functional/page_objects/home_page.ts index 31a162a8800d6..4bdf99a9b7b35 100644 --- a/test/functional/page_objects/home_page.ts +++ b/test/functional/page_objects/home_page.ts @@ -91,40 +91,46 @@ export class HomePageObject extends FtrService { async addSampleDataSet(id: string) { await this.openSampleDataAccordion(); - const isInstalled = await this.isSampleDataSetInstalled(id); - if (!isInstalled) { + await this.retry.waitFor('sample data to be installed', async () => { + // count for the edge case where some how installation completes just before the retry occurs + if (await this.isSampleDataSetInstalled(id)) { + return true; + } + this.log.debug(`Attempting to add sample data: ${id}`); - await this.retry.waitFor('sample data to be installed', async () => { - // Echoing the adjustments made to 'removeSampleDataSet', as we are seeing flaky test cases here as well - // https://github.com/elastic/kibana/issues/52714 - await this.testSubjects.waitForEnabled(`addSampleDataSet${id}`); - await this.common.sleep(1010); - await this.testSubjects.click(`addSampleDataSet${id}`); - await this.common.sleep(1010); - await this._waitForSampleDataLoadingAction(id); - return await this.isSampleDataSetInstalled(id); - }); - } + + // Echoing the adjustments made to 'removeSampleDataSet', as we are seeing flaky test cases here as well + // https://github.com/elastic/kibana/issues/52714 + await this.testSubjects.waitForEnabled(`addSampleDataSet${id}`); + await this.common.sleep(1010); + await this.testSubjects.click(`addSampleDataSet${id}`); + await this.common.sleep(1010); + await this._waitForSampleDataLoadingAction(id); + return await this.isSampleDataSetInstalled(id); + }); } async removeSampleDataSet(id: string) { await this.openSampleDataAccordion(); - const isInstalled = await this.isSampleDataSetInstalled(id); - if (isInstalled) { + await this.retry.waitFor('sample data to be removed', async () => { + // account for the edge case where some how data is uninstalled just before the retry occurs + if (!(await this.isSampleDataSetInstalled(id))) { + return true; + } + this.log.debug(`Attempting to remove sample data: ${id}`); - await this.retry.waitFor('sample data to be removed', async () => { - // looks like overkill but we're hitting flaky cases where we click but it doesn't remove - await this.testSubjects.waitForEnabled(`removeSampleDataSet${id}`); - // https://github.com/elastic/kibana/issues/65949 - // Even after waiting for the "Remove" button to be enabled we still have failures - // where it appears the click just didn't work. - await this.common.sleep(1010); - await this.testSubjects.click(`removeSampleDataSet${id}`); - await this.common.sleep(1010); - await this._waitForSampleDataLoadingAction(id); - return !(await this.isSampleDataSetInstalled(id)); - }); - } + + // looks like overkill but we're hitting flaky cases where we click but it doesn't remove + await this.testSubjects.waitForEnabled(`removeSampleDataSet${id}`); + // https://github.com/elastic/kibana/issues/65949 + // Even after waiting for the "Remove" button to be enabled we still have failures + // where it appears the click just didn't work. + await this.common.sleep(1010); + await this.testSubjects.click(`removeSampleDataSet${id}`); + await this.common.sleep(1010); + await this._waitForSampleDataLoadingAction(id); + return !(await this.isSampleDataSetInstalled(id)); + }); } // loading action is either uninstall and install diff --git a/tsconfig.base.json b/tsconfig.base.json index 4471cb1bc6754..7b1bc834fcc28 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1044,6 +1044,8 @@ "@kbn/index-patterns-test-plugin/*": ["test/plugin_functional/plugins/index_patterns/*"], "@kbn/inference_integration_flyout": ["x-pack/packages/ml/inference_integration_flyout"], "@kbn/inference_integration_flyout/*": ["x-pack/packages/ml/inference_integration_flyout/*"], + "@kbn/inference-common": ["x-pack/packages/ai-infra/inference-common"], + "@kbn/inference-common/*": ["x-pack/packages/ai-infra/inference-common/*"], "@kbn/inference-plugin": ["x-pack/plugins/inference"], "@kbn/inference-plugin/*": ["x-pack/plugins/inference/*"], "@kbn/infra-forge": ["x-pack/packages/kbn-infra-forge"], @@ -1578,8 +1580,6 @@ "@kbn/security-plugin-types-server/*": ["x-pack/packages/security/plugin_types_server/*"], "@kbn/security-role-management-model": ["x-pack/packages/security/role_management_model"], "@kbn/security-role-management-model/*": ["x-pack/packages/security/role_management_model/*"], - "@kbn/security-solution-common": ["x-pack/packages/security-solution/common"], - "@kbn/security-solution-common/*": ["x-pack/packages/security-solution/common/*"], "@kbn/security-solution-distribution-bar": ["x-pack/packages/security-solution/distribution_bar"], "@kbn/security-solution-distribution-bar/*": ["x-pack/packages/security-solution/distribution_bar/*"], "@kbn/security-solution-ess": ["x-pack/plugins/security_solution_ess"], diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 7afbc9dc704c4..e1e8478aa0517 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -3,6 +3,7 @@ "paths": { "xpack.actions": "plugins/actions", "xpack.aiops": [ + "packages/ml/aiops_common", "packages/ml/aiops_components", "packages/ml/aiops_log_pattern_analysis", "packages/ml/aiops_log_rate_analysis", diff --git a/x-pack/packages/ai-infra/inference-common/README.md b/x-pack/packages/ai-infra/inference-common/README.md new file mode 100644 index 0000000000000..f16f1ce9cea75 --- /dev/null +++ b/x-pack/packages/ai-infra/inference-common/README.md @@ -0,0 +1,7 @@ +# @kbn/inference-common + +Common types and utilities for the inference APIs and features. + +The main purpose of the package is to have a clean line between the inference plugin's +implementation and the underlying types, so that other packages or plugins can leverage the +types without directly depending on the plugin. diff --git a/x-pack/packages/ai-infra/inference-common/index.ts b/x-pack/packages/ai-infra/inference-common/index.ts new file mode 100644 index 0000000000000..502d8e86a0beb --- /dev/null +++ b/x-pack/packages/ai-infra/inference-common/index.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { + MessageRole, + ChatCompletionEventType, + ToolChoiceType, + type Message, + type AssistantMessage, + type ToolMessage, + type UserMessage, + type ToolSchemaType, + type FromToolSchema, + type ToolSchema, + type UnvalidatedToolCall, + type ToolCallsOf, + type ToolCall, + type ToolDefinition, + type ToolOptions, + type FunctionCallingMode, + type ToolChoice, + type ChatCompleteAPI, + type ChatCompleteOptions, + type ChatCompleteCompositeResponse, + type ChatCompletionTokenCountEvent, + type ChatCompletionEvent, + type ChatCompletionChunkEvent, + type ChatCompletionChunkToolCall, + type ChatCompletionMessageEvent, + type ChatCompleteStreamResponse, + type ChatCompleteResponse, + type ChatCompletionTokenCount, + withoutTokenCountEvents, + withoutChunkEvents, + isChatCompletionMessageEvent, + isChatCompletionEvent, + isChatCompletionChunkEvent, + isChatCompletionTokenCountEvent, + ChatCompletionErrorCode, + type ChatCompletionToolNotFoundError, + type ChatCompletionToolValidationError, + type ChatCompletionTokenLimitReachedError, + isToolValidationError, + isTokenLimitReachedError, + isToolNotFoundError, +} from './src/chat_complete'; +export { + OutputEventType, + type OutputAPI, + type OutputOptions, + type OutputResponse, + type OutputCompositeResponse, + type OutputStreamResponse, + type OutputCompleteEvent, + type OutputUpdateEvent, + type Output, + type OutputEvent, + isOutputCompleteEvent, + isOutputUpdateEvent, + isOutputEvent, + withoutOutputUpdateEvents, +} from './src/output'; +export { + InferenceTaskEventType, + type InferenceTaskEvent, + type InferenceTaskEventBase, +} from './src/inference_task'; +export { + InferenceTaskError, + InferenceTaskErrorCode, + type InferenceTaskErrorEvent, + type InferenceTaskInternalError, + type InferenceTaskRequestError, + createInferenceInternalError, + createInferenceRequestError, + isInferenceError, + isInferenceInternalError, + isInferenceRequestError, +} from './src/errors'; diff --git a/x-pack/packages/security-solution/common/jest.config.js b/x-pack/packages/ai-infra/inference-common/jest.config.js similarity index 83% rename from x-pack/packages/security-solution/common/jest.config.js rename to x-pack/packages/ai-infra/inference-common/jest.config.js index 7047e229df092..faa0d30b40233 100644 --- a/x-pack/packages/security-solution/common/jest.config.js +++ b/x-pack/packages/ai-infra/inference-common/jest.config.js @@ -8,5 +8,5 @@ module.exports = { preset: '@kbn/test', rootDir: '../../../..', - roots: ['/x-pack/packages/security-solution/common'], + roots: ['/x-pack/packages/ai-infra/inference-common'], }; diff --git a/x-pack/packages/ai-infra/inference-common/kibana.jsonc b/x-pack/packages/ai-infra/inference-common/kibana.jsonc new file mode 100644 index 0000000000000..568755d303c3b --- /dev/null +++ b/x-pack/packages/ai-infra/inference-common/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/inference-common", + "owner": "@elastic/appex-ai-infra" +} diff --git a/x-pack/packages/ai-infra/inference-common/package.json b/x-pack/packages/ai-infra/inference-common/package.json new file mode 100644 index 0000000000000..0c67ca7815f16 --- /dev/null +++ b/x-pack/packages/ai-infra/inference-common/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/inference-common", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0", + "sideEffects": false +} diff --git a/x-pack/packages/ai-infra/inference-common/src/chat_complete/api.ts b/x-pack/packages/ai-infra/inference-common/src/chat_complete/api.ts new file mode 100644 index 0000000000000..cb91f4e53e8ae --- /dev/null +++ b/x-pack/packages/ai-infra/inference-common/src/chat_complete/api.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 { Observable } from 'rxjs'; +import type { ToolCallsOf, ToolOptions } from './tools'; +import type { Message } from './messages'; +import type { ChatCompletionEvent, ChatCompletionTokenCount } from './events'; + +/** + * Request a completion from the LLM based on a prompt or conversation. + * + * By default, The complete LLM response will be returned as a promise. + * + * @example using the API in default mode to get promise of the LLM response. + * ```ts + * const response = await chatComplete({ + * connectorId: 'my-connector', + * system: "You are a helpful assistant", + * messages: [ + * { role: MessageRole.User, content: "Some question?"}, + * ] + * }); + * + * const { content, tokens, toolCalls } = response; + * ``` + * + * Use `stream: true` to return an observable returning the full set + * of events in real time. + * + * @example using the API in stream mode to get an event observable. + * ```ts + * const events$ = chatComplete({ + * stream: true, + * connectorId: 'my-connector', + * system: "You are a helpful assistant", + * messages: [ + * { role: MessageRole.User, content: "First question?"}, + * { role: MessageRole.Assistant, content: "Some answer"}, + * { role: MessageRole.User, content: "Another question?"}, + * ] + * }); + * + * // using the observable + * events$.pipe(withoutTokenCountEvents()).subscribe((event) => { + * if (isChatCompletionChunkEvent(event)) { + * // do something with the chunk event + * } else { + * // do something with the message event + * } + * }); + * ``` + */ +export type ChatCompleteAPI = < + TToolOptions extends ToolOptions = ToolOptions, + TStream extends boolean = false +>( + options: ChatCompleteOptions +) => ChatCompleteCompositeResponse; + +/** + * Options used to call the {@link ChatCompleteAPI} + */ +export type ChatCompleteOptions< + TToolOptions extends ToolOptions = ToolOptions, + TStream extends boolean = false +> = { + /** + * The ID of the connector to use. + * Must be an inference connector, or an error will be thrown. + */ + connectorId: string; + /** + * Set to true to enable streaming, which will change the API response type from + * a single {@link ChatCompleteResponse} promise + * to a {@link ChatCompleteStreamResponse} event observable. + * + * Defaults to false. + */ + stream?: TStream; + /** + * Optional system message for the LLM. + */ + system?: string; + /** + * The list of messages for the current conversation + */ + messages: Message[]; + /** + * Function calling mode, defaults to "native". + */ + functionCalling?: FunctionCallingMode; +} & TToolOptions; + +/** + * Composite response type from the {@link ChatCompleteAPI}, + * which can be either an observable or a promise depending on + * whether API was called with stream mode enabled or not. + */ +export type ChatCompleteCompositeResponse< + TToolOptions extends ToolOptions = ToolOptions, + TStream extends boolean = false +> = TStream extends true + ? ChatCompleteStreamResponse + : Promise>; + +/** + * Response from the {@link ChatCompleteAPI} when streaming is enabled. + * + * Observable of {@link ChatCompletionEvent} + */ +export type ChatCompleteStreamResponse = Observable< + ChatCompletionEvent +>; + +/** + * Response from the {@link ChatCompleteAPI} when streaming is not enabled. + */ +export interface ChatCompleteResponse { + /** + * The text content of the LLM response. + */ + content: string; + /** + * The eventual tool calls performed by the LLM. + */ + toolCalls: ToolCallsOf['toolCalls']; + /** + * Token counts + */ + tokens?: ChatCompletionTokenCount; +} + +/** + * Define the function calling mode when using inference APIs. + * - native will use the LLM's native function calling (requires the LLM to have native support) + * - simulated: will emulate function calling with function calling instructions + */ +export type FunctionCallingMode = 'native' | 'simulated'; diff --git a/x-pack/plugins/inference/common/chat_complete/errors.ts b/x-pack/packages/ai-infra/inference-common/src/chat_complete/errors.ts similarity index 61% rename from x-pack/plugins/inference/common/chat_complete/errors.ts rename to x-pack/packages/ai-infra/inference-common/src/chat_complete/errors.ts index 8497350d7b49b..b9d859a666761 100644 --- a/x-pack/plugins/inference/common/chat_complete/errors.ts +++ b/x-pack/packages/ai-infra/inference-common/src/chat_complete/errors.ts @@ -5,16 +5,22 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; import { InferenceTaskError } from '../errors'; import type { UnvalidatedToolCall } from './tools'; +/** + * List of code of error that are specific to the {@link ChatCompleteAPI} + */ export enum ChatCompletionErrorCode { TokenLimitReachedError = 'tokenLimitReachedError', ToolNotFoundError = 'toolNotFoundError', ToolValidationError = 'toolValidationError', } +/** + * Error thrown if the completion call fails because of a token limit + * error, e.g. when the context window is higher than the limit + */ export type ChatCompletionTokenLimitReachedError = InferenceTaskError< ChatCompletionErrorCode.TokenLimitReachedError, { @@ -23,13 +29,24 @@ export type ChatCompletionTokenLimitReachedError = InferenceTaskError< } >; +/** + * Error thrown if the LLM called a tool that was not provided + * in the list of available tools. + */ export type ChatCompletionToolNotFoundError = InferenceTaskError< ChatCompletionErrorCode.ToolNotFoundError, { + /** The name of the tool that got called */ name: string; } >; +/** + * Error thrown when the LLM called a tool with parameters that + * don't match the tool's schema. + * + * The level of details on the error vary depending on the underlying LLM. + */ export type ChatCompletionToolValidationError = InferenceTaskError< ChatCompletionErrorCode.ToolValidationError, { @@ -40,42 +57,9 @@ export type ChatCompletionToolValidationError = InferenceTaskError< } >; -export function createTokenLimitReachedError( - tokenLimit?: number, - tokenCount?: number -): ChatCompletionTokenLimitReachedError { - return new InferenceTaskError( - ChatCompletionErrorCode.TokenLimitReachedError, - i18n.translate('xpack.inference.chatCompletionError.tokenLimitReachedError', { - defaultMessage: `Token limit reached. Token limit is {tokenLimit}, but the current conversation has {tokenCount} tokens.`, - values: { tokenLimit, tokenCount }, - }), - { tokenLimit, tokenCount } - ); -} - -export function createToolNotFoundError(name: string): ChatCompletionToolNotFoundError { - return new InferenceTaskError( - ChatCompletionErrorCode.ToolNotFoundError, - `Tool ${name} called but was not available`, - { - name, - } - ); -} - -export function createToolValidationError( - message: string, - meta: { - name?: string; - arguments?: string; - errorsText?: string; - toolCalls?: UnvalidatedToolCall[]; - } -): ChatCompletionToolValidationError { - return new InferenceTaskError(ChatCompletionErrorCode.ToolValidationError, message, meta); -} - +/** + * Check if an error is a {@link ChatCompletionToolValidationError} + */ export function isToolValidationError(error?: Error): error is ChatCompletionToolValidationError { return ( error instanceof InferenceTaskError && @@ -83,6 +67,9 @@ export function isToolValidationError(error?: Error): error is ChatCompletionToo ); } +/** + * Check if an error is a {@link ChatCompletionTokenLimitReachedError} + */ export function isTokenLimitReachedError( error: Error ): error is ChatCompletionTokenLimitReachedError { @@ -92,6 +79,9 @@ export function isTokenLimitReachedError( ); } +/** + * Check if an error is a {@link ChatCompletionToolNotFoundError} + */ export function isToolNotFoundError(error: Error): error is ChatCompletionToolNotFoundError { return ( error instanceof InferenceTaskError && error.code === ChatCompletionErrorCode.ToolNotFoundError diff --git a/x-pack/packages/ai-infra/inference-common/src/chat_complete/event_utils.ts b/x-pack/packages/ai-infra/inference-common/src/chat_complete/event_utils.ts new file mode 100644 index 0000000000000..4749673264aff --- /dev/null +++ b/x-pack/packages/ai-infra/inference-common/src/chat_complete/event_utils.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 { filter, OperatorFunction } from 'rxjs'; +import { InferenceTaskEvent } from '../inference_task'; +import { + ChatCompletionEventType, + ChatCompletionEvent, + ChatCompletionChunkEvent, + ChatCompletionMessageEvent, + ChatCompletionTokenCountEvent, +} from './events'; +import type { ToolOptions } from './tools'; + +/** + * Check if the provided {@link ChatCompletionEvent} is a {@link ChatCompletionChunkEvent} + */ +export function isChatCompletionChunkEvent( + event: ChatCompletionEvent +): event is ChatCompletionChunkEvent { + return event.type === ChatCompletionEventType.ChatCompletionChunk; +} + +/** + * Check if the provided {@link ChatCompletionEvent} is a {@link ChatCompletionMessageEvent} + */ +export function isChatCompletionMessageEvent( + event: ChatCompletionEvent +): event is ChatCompletionMessageEvent { + return event.type === ChatCompletionEventType.ChatCompletionMessage; +} + +/** + * Check if the provided {@link ChatCompletionEvent} is a {@link ChatCompletionMessageEvent} + */ +export function isChatCompletionTokenCountEvent( + event: ChatCompletionEvent +): event is ChatCompletionTokenCountEvent { + return event.type === ChatCompletionEventType.ChatCompletionTokenCount; +} + +/** + * Check if the provided {@link InferenceTaskEvent} is a {@link ChatCompletionEvent} + */ +export function isChatCompletionEvent(event: InferenceTaskEvent): event is ChatCompletionEvent { + return ( + event.type === ChatCompletionEventType.ChatCompletionChunk || + event.type === ChatCompletionEventType.ChatCompletionMessage || + event.type === ChatCompletionEventType.ChatCompletionTokenCount + ); +} + +/** + * Operator filtering out the chunk events from the provided observable. + */ +export function withoutChunkEvents(): OperatorFunction< + T, + Exclude +> { + return filter( + (event): event is Exclude => + event.type !== ChatCompletionEventType.ChatCompletionChunk + ); +} + +/** + * Operator filtering out the token count events from the provided observable. + */ +export function withoutTokenCountEvents(): OperatorFunction< + T, + Exclude +> { + return filter( + (event): event is Exclude => + event.type !== ChatCompletionEventType.ChatCompletionTokenCount + ); +} diff --git a/x-pack/packages/ai-infra/inference-common/src/chat_complete/events.ts b/x-pack/packages/ai-infra/inference-common/src/chat_complete/events.ts new file mode 100644 index 0000000000000..73396b3e2b905 --- /dev/null +++ b/x-pack/packages/ai-infra/inference-common/src/chat_complete/events.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 { InferenceTaskEventBase } from '../inference_task'; +import type { ToolCallsOf, ToolOptions } from './tools'; + +/** + * List possible values of {@link ChatCompletionEvent} types. + */ +export enum ChatCompletionEventType { + ChatCompletionChunk = 'chatCompletionChunk', + ChatCompletionTokenCount = 'chatCompletionTokenCount', + ChatCompletionMessage = 'chatCompletionMessage', +} + +/** + * Message event, sent only once, after all the chunks were emitted, and containing + * the whole text content and potential tool calls of the response. + */ +export type ChatCompletionMessageEvent = + InferenceTaskEventBase & { + /** + * The text content of the LLM response. + */ + content: string; + /** + * The eventual tool calls performed by the LLM. + */ + toolCalls: ToolCallsOf['toolCalls']; + }; + +/** + * Represent a partial tool call present in a chunk event. + * + * Note that all properties of the structure, except from the index, + * are partial and must be aggregated. + */ +export interface ChatCompletionChunkToolCall { + /** + * The tool call index (position in the tool call array). + */ + index: number; + /** + * chunk of tool call id. + */ + toolCallId: string; + function: { + /** + * chunk of tool name. + */ + name: string; + /** + * chunk of tool call arguments. + */ + arguments: string; + }; +} + +/** + * Chunk event, containing a fragment of the total content, + * and potentially chunks of tool calls. + */ +export type ChatCompletionChunkEvent = + InferenceTaskEventBase & { + /** + * The content chunk + */ + content: string; + /** + * The tool call chunks + */ + tool_calls: ChatCompletionChunkToolCall[]; + }; + +/** + * Token count structure for the chatComplete API. + */ +export interface ChatCompletionTokenCount { + /** + * Input token count + */ + prompt: number; + /** + * Output token count + */ + completion: number; + /** + * Total token count + */ + total: number; +} + +/** + * Token count event, send only once, usually (but not necessarily) + * before the message event + */ +export type ChatCompletionTokenCountEvent = + InferenceTaskEventBase & { + /** + * The token count structure + */ + tokens: ChatCompletionTokenCount; + }; + +/** + * Events emitted from the {@link ChatCompleteResponse} observable + * returned from the {@link ChatCompleteAPI}. + * + * The chatComplete API returns 3 type of events: + * - {@link ChatCompletionChunkEvent}: message chunk events + * - {@link ChatCompletionTokenCountEvent}: token count event + * - {@link ChatCompletionMessageEvent}: message event + * + * Note that chunk events can be emitted any amount of times, but token count will be emitted + * at most once (could not be emitted depending on the underlying connector), and message + * event will be emitted ex + * + */ +export type ChatCompletionEvent = + | ChatCompletionChunkEvent + | ChatCompletionTokenCountEvent + | ChatCompletionMessageEvent; diff --git a/x-pack/packages/ai-infra/inference-common/src/chat_complete/index.ts b/x-pack/packages/ai-infra/inference-common/src/chat_complete/index.ts new file mode 100644 index 0000000000000..ca69f39b273e5 --- /dev/null +++ b/x-pack/packages/ai-infra/inference-common/src/chat_complete/index.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. + */ + +export type { + ChatCompleteCompositeResponse, + ChatCompleteAPI, + ChatCompleteOptions, + FunctionCallingMode, + ChatCompleteStreamResponse, + ChatCompleteResponse, +} from './api'; +export { + ChatCompletionEventType, + type ChatCompletionMessageEvent, + type ChatCompletionChunkEvent, + type ChatCompletionEvent, + type ChatCompletionChunkToolCall, + type ChatCompletionTokenCountEvent, + type ChatCompletionTokenCount, +} from './events'; +export { + MessageRole, + type Message, + type AssistantMessage, + type UserMessage, + type ToolMessage, +} from './messages'; +export { type ToolSchema, type ToolSchemaType, type FromToolSchema } from './tool_schema'; +export { + ToolChoiceType, + type ToolOptions, + type ToolDefinition, + type ToolCall, + type ToolCallsOf, + type UnvalidatedToolCall, + type ToolChoice, +} from './tools'; +export { + isChatCompletionChunkEvent, + isChatCompletionEvent, + isChatCompletionMessageEvent, + isChatCompletionTokenCountEvent, + withoutChunkEvents, + withoutTokenCountEvents, +} from './event_utils'; +export { + ChatCompletionErrorCode, + type ChatCompletionToolNotFoundError, + type ChatCompletionToolValidationError, + type ChatCompletionTokenLimitReachedError, + isToolValidationError, + isTokenLimitReachedError, + isToolNotFoundError, +} from './errors'; diff --git a/x-pack/packages/ai-infra/inference-common/src/chat_complete/messages.ts b/x-pack/packages/ai-infra/inference-common/src/chat_complete/messages.ts new file mode 100644 index 0000000000000..ca74b094e0a3b --- /dev/null +++ b/x-pack/packages/ai-infra/inference-common/src/chat_complete/messages.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ToolCall } from './tools'; + +/** + * Enum for all possible {@link Message} roles. + */ +export enum MessageRole { + User = 'user', + Assistant = 'assistant', + Tool = 'tool', +} + +/** + * Base type for all subtypes of {@link Message}. + */ +interface MessageBase { + role: TRole; +} + +/** + * Represents a message from the user. + */ +export type UserMessage = MessageBase & { + /** + * The text content of the user message + */ + content: string; +}; + +/** + * Represents a message from the LLM. + */ +export type AssistantMessage = MessageBase & { + /** + * The text content of the message. + * Can be null if the LLM called a tool. + */ + content: string | null; + /** + * A potential list of {@ToolCall} the LLM asked to execute. + * Note that LLM with parallel tool invocation can potentially call multiple tools at the same time. + */ + toolCalls?: ToolCall[]; +}; + +/** + * Represents a tool invocation result, following a request from the LLM to execute a tool. + */ +export type ToolMessage | unknown> = + MessageBase & { + /** + * The call id matching the {@link ToolCall} this tool message is for. + */ + toolCallId: string; + /** + * The response from the tool invocation. + */ + response: TToolResponse; + }; + +/** + * Mixin composed of all the possible types of messages in a chatComplete discussion. + * + * Message can be of three types: + * - {@link UserMessage} + * - {@link AssistantMessage} + * - {@link ToolMessage} + */ +export type Message = UserMessage | AssistantMessage | ToolMessage; diff --git a/x-pack/plugins/inference/common/chat_complete/tool_schema.ts b/x-pack/packages/ai-infra/inference-common/src/chat_complete/tool_schema.ts similarity index 89% rename from x-pack/plugins/inference/common/chat_complete/tool_schema.ts rename to x-pack/packages/ai-infra/inference-common/src/chat_complete/tool_schema.ts index 2a2c61f8e9b70..fd935785f74f5 100644 --- a/x-pack/plugins/inference/common/chat_complete/tool_schema.ts +++ b/x-pack/packages/ai-infra/inference-common/src/chat_complete/tool_schema.ts @@ -11,9 +11,9 @@ interface ToolSchemaFragmentBase { description?: string; } -interface ToolSchemaTypeObject extends ToolSchemaFragmentBase { +export interface ToolSchemaTypeObject extends ToolSchemaFragmentBase { type: 'object'; - properties?: Record; + properties: Record; required?: string[] | readonly string[]; } @@ -40,6 +40,9 @@ interface ToolSchemaTypeArray extends ToolSchemaFragmentBase { items: Exclude; } +/** + * A tool schema property's possible types. + */ export type ToolSchemaType = | ToolSchemaTypeObject | ToolSchemaTypeString @@ -72,8 +75,14 @@ type FromToolSchemaString = ? ValuesType : string; +/** + * Defines the schema for a {@link ToolDefinition} + */ export type ToolSchema = ToolSchemaTypeObject; +/** + * Utility type to infer the shape of a tool call from its schema. + */ export type FromToolSchema = TToolSchema extends ToolSchemaTypeObject ? FromToolSchemaObject diff --git a/x-pack/plugins/inference/common/chat_complete/tools.ts b/x-pack/packages/ai-infra/inference-common/src/chat_complete/tools.ts similarity index 53% rename from x-pack/plugins/inference/common/chat_complete/tools.ts rename to x-pack/packages/ai-infra/inference-common/src/chat_complete/tools.ts index a5db86c7c996d..0c7d5c6755f31 100644 --- a/x-pack/plugins/inference/common/chat_complete/tools.ts +++ b/x-pack/packages/ai-infra/inference-common/src/chat_complete/tools.ts @@ -4,15 +4,12 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import type { ValuesType } from 'utility-types'; import { FromToolSchema, ToolSchema } from './tool_schema'; type Assert = TValue extends TType ? TValue & TType : never; -interface CustomToolChoice { - function: TName; -} - type ToolsOfChoice = TToolOptions['toolChoice'] extends { function: infer TToolName; } @@ -21,6 +18,9 @@ type ToolsOfChoice = TToolOptions['toolChoice' : TToolOptions['tools'] : TToolOptions['tools']; +/** + * Utility type to infer the tool calls response shape. + */ type ToolResponsesOf | undefined> = TTools extends Record ? Array< @@ -30,18 +30,64 @@ type ToolResponsesOf | undefined> > : never[]; +/** + * Utility type to infer the tool call response shape. + */ type ToolResponseOf = ToolCall< TName, TToolDefinition extends { schema: ToolSchema } ? FromToolSchema : {} >; +/** + * Tool invocation choice type. + * + * Refer to {@link ToolChoice} for more details. + */ +export enum ToolChoiceType { + none = 'none', + auto = 'auto', + required = 'required', +} + +/** + * Represent a tool choice where the LLM is forced to call a specific tool. + * + * Refer to {@link ToolChoice} for more details. + */ +interface CustomToolChoice { + function: TName; +} + +/** + * Defines the tool invocation for {@link ToolOptions}, either a {@link ToolChoiceType} or {@link CustomToolChoice}. + * - {@link ToolChoiceType.none}: the LLM will never call a tool + * - {@link ToolChoiceType.auto}: the LLM will decide if it should call a tool or provide a text response + * - {@link ToolChoiceType.required}: the LLM will always call a tool, but will decide with one to call + * - {@link CustomToolChoice}: the LLM will always call the specified tool + */ export type ToolChoice = ToolChoiceType | CustomToolChoice; +/** + * The definition of a tool that will be provided to the LLM for it to eventually call. + */ export interface ToolDefinition { + /** + * A description of what the tool does. Note that this will be exposed to the LLM, + * so the description should be explicit about what the tool does and when to call it. + */ description: string; + /** + * The input schema for the tool, representing the shape of the tool's parameters + * + * Even if optional, it is highly recommended to define a schema for all tool definitions, unless + * the tool is supposed to be called without parameters. + */ schema?: ToolSchema; } +/** + * Utility type to infer the toolCall type of {@link ChatCompletionMessageEvent}. + */ export type ToolCallsOf = TToolOptions extends { tools?: Record; } @@ -52,12 +98,11 @@ export type ToolCallsOf = TToolOptions extends } : { toolCalls: never }; -export enum ToolChoiceType { - none = 'none', - auto = 'auto', - required = 'required', -} - +/** + * Represents a tool call from the LLM before correctly converted to the schema type. + * + * Only publicly exposed because referenced by {@link ChatCompletionToolValidationError} + */ export interface UnvalidatedToolCall { toolCallId: string; function: { @@ -66,17 +111,39 @@ export interface UnvalidatedToolCall { }; } +/** + * Represents a tool call performed by the LLM. + */ export interface ToolCall< TName extends string = string, TArguments extends Record | undefined = Record | undefined > { + /** + * The id of the tool call, that must be re-used when providing the tool call response + */ toolCallId: string; function: { + /** + * The name of the tool that was called + */ name: TName; } & (TArguments extends Record ? { arguments: TArguments } : {}); } +/** + * Tool-related parameters of {@link ChatCompleteAPI} + */ export interface ToolOptions { + /** + * The choice of tool execution. + * + * Refer to {@link ToolChoice} + */ toolChoice?: ToolChoice; + /** + * The list of tool definitions that will be exposed to the LLM. + * + * Refer to {@link ToolDefinition}. + */ tools?: Record; } diff --git a/x-pack/plugins/inference/common/errors.ts b/x-pack/packages/ai-infra/inference-common/src/errors.ts similarity index 93% rename from x-pack/plugins/inference/common/errors.ts rename to x-pack/packages/ai-infra/inference-common/src/errors.ts index e8bcd4cf60aaf..5a99adc4321d9 100644 --- a/x-pack/plugins/inference/common/errors.ts +++ b/x-pack/packages/ai-infra/inference-common/src/errors.ts @@ -4,14 +4,20 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { i18n } from '@kbn/i18n'; + import { InferenceTaskEventBase, InferenceTaskEventType } from './inference_task'; +/** + * Enum for generic inference error codes. + */ export enum InferenceTaskErrorCode { internalError = 'internalError', requestError = 'requestError', } +/** + * Base class for all inference API errors. + */ export class InferenceTaskError< TCode extends string, TMeta extends Record | undefined @@ -51,9 +57,7 @@ export type InferenceTaskRequestError = InferenceTaskError< >; export function createInferenceInternalError( - message: string = i18n.translate('xpack.inference.internalError', { - defaultMessage: 'An internal error occurred', - }), + message = 'An internal error occurred', meta?: Record ): InferenceTaskInternalError { return new InferenceTaskError(InferenceTaskErrorCode.internalError, message, meta ?? {}); diff --git a/x-pack/plugins/inference/common/inference_task.ts b/x-pack/packages/ai-infra/inference-common/src/inference_task.ts similarity index 81% rename from x-pack/plugins/inference/common/inference_task.ts rename to x-pack/packages/ai-infra/inference-common/src/inference_task.ts index 7b8f65b7af2c9..15449e1275a5b 100644 --- a/x-pack/plugins/inference/common/inference_task.ts +++ b/x-pack/packages/ai-infra/inference-common/src/inference_task.ts @@ -5,7 +5,13 @@ * 2.0. */ +/** + * Base interface for all inference events. + */ export interface InferenceTaskEventBase { + /** + * Unique identifier of the event type. + */ type: TEventType; } diff --git a/x-pack/packages/ai-infra/inference-common/src/output/api.ts b/x-pack/packages/ai-infra/inference-common/src/output/api.ts new file mode 100644 index 0000000000000..3355042910a61 --- /dev/null +++ b/x-pack/packages/ai-infra/inference-common/src/output/api.ts @@ -0,0 +1,147 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Observable } from 'rxjs'; +import { Message, FunctionCallingMode, FromToolSchema, ToolSchema } from '../chat_complete'; +import { Output, OutputEvent } from './events'; + +/** + * Generate a response with the LLM for a prompt, optionally based on a schema. + * + * @example + * ```ts + * // schema must be defined as full const or using the `satisfies ToolSchema` modifier for TS type inference to work + * const mySchema = { + * type: 'object', + * properties: { + * animals: { + * description: 'the list of animals that are mentioned in the provided article', + * type: 'array', + * items: { + * type: 'string', + * }, + * }, + * }, + * } as const; + * + * const response = outputApi({ + * id: 'extract_from_article', + * connectorId: 'my-connector connector', + * schema: mySchema, + * input: ` + * Please find all the animals that are mentioned in the following document: + * ## Document¬ + * ${theDoc} + * `, + * }); + * + * // output is properly typed from the provided schema + * const { animals } = response.output; + * ``` + */ +export type OutputAPI = < + TId extends string = string, + TOutputSchema extends ToolSchema | undefined = ToolSchema | undefined, + TStream extends boolean = false +>( + options: OutputOptions +) => OutputCompositeResponse; + +/** + * Options for the {@link OutputAPI} + */ +export interface OutputOptions< + TId extends string = string, + TOutputSchema extends ToolSchema | undefined = ToolSchema | undefined, + TStream extends boolean = false +> { + /** + * The id of the operation. + */ + id: TId; + /** + * The ID of the connector to use. + * Must be an inference connector, or an error will be thrown. + */ + connectorId: string; + /** + * Optional system message for the LLM. + */ + system?: string; + /** + * The prompt for the LLM. + */ + input: string; + /** + * The schema the response from the LLM should adhere to. + */ + schema?: TOutputSchema; + /** + * Previous messages in the conversation. + * If provided, will be passed to the LLM in addition to `input`. + */ + previousMessages?: Message[]; + /** + * Function calling mode, defaults to "native". + */ + functionCalling?: FunctionCallingMode; + /** + * Set to true to enable streaming, which will change the API response type from + * a single promise to an event observable. + * + * Defaults to false. + */ + stream?: TStream; +} + +/** + * Composite response type from the {@link OutputAPI}, + * which can be either an observable or a promise depending on + * whether API was called with stream mode enabled or not. + */ +export type OutputCompositeResponse< + TId extends string = string, + TOutputSchema extends ToolSchema | undefined = ToolSchema | undefined, + TStream extends boolean = false +> = TStream extends true + ? OutputStreamResponse + : Promise< + OutputResponse< + TId, + TOutputSchema extends ToolSchema ? FromToolSchema : undefined + > + >; + +/** + * Response from the {@link OutputAPI} when streaming is not enabled. + */ +export interface OutputResponse { + /** + * The id of the operation, as specified when calling the API. + */ + id: TId; + /** + * The task output, following the schema specified as input. + */ + output: TOutput; + /** + * Potential text content provided by the LLM, if it was provided in addition to the tool call. + */ + content: string; +} + +/** + * Response from the {@link OutputAPI} in streaming mode. + * + * @returns Observable of {@link OutputEvent} + */ +export type OutputStreamResponse< + TId extends string = string, + TOutputSchema extends ToolSchema | undefined = ToolSchema | undefined +> = Observable< + OutputEvent : undefined> +>; diff --git a/x-pack/packages/ai-infra/inference-common/src/output/event_utils.ts b/x-pack/packages/ai-infra/inference-common/src/output/event_utils.ts new file mode 100644 index 0000000000000..1139bac92c610 --- /dev/null +++ b/x-pack/packages/ai-infra/inference-common/src/output/event_utils.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 { filter, OperatorFunction } from 'rxjs'; +import { OutputCompleteEvent, OutputEvent, OutputEventType, OutputUpdateEvent } from '.'; +import type { InferenceTaskEvent } from '../inference_task'; + +/** + * Check if the provided {@link ChatCompletionEvent} is a {@link ChatCompletionChunkEvent} + */ +export function isOutputCompleteEvent( + event: TOutputEvent +): event is Extract { + return event.type === OutputEventType.OutputComplete; +} + +/** + * Check if the provided {@link InferenceTaskEvent} is a {@link OutputEvent} + */ +export function isOutputEvent(event: InferenceTaskEvent): event is OutputEvent { + return ( + event.type === OutputEventType.OutputComplete || event.type === OutputEventType.OutputUpdate + ); +} + +/** + * Check if the provided {@link OutputEvent} is a {@link OutputUpdateEvent} + */ +export function isOutputUpdateEvent( + event: OutputEvent +): event is OutputUpdateEvent { + return event.type === OutputEventType.OutputComplete; +} + +/** + * Operator filtering out the update events from the provided observable. + */ +export function withoutOutputUpdateEvents(): OperatorFunction< + T, + Exclude +> { + return filter( + (event): event is Exclude => event.type !== OutputEventType.OutputUpdate + ); +} diff --git a/x-pack/packages/ai-infra/inference-common/src/output/events.ts b/x-pack/packages/ai-infra/inference-common/src/output/events.ts new file mode 100644 index 0000000000000..794f58bd7db79 --- /dev/null +++ b/x-pack/packages/ai-infra/inference-common/src/output/events.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 { InferenceTaskEventBase } from '../inference_task'; + +/** + * List possible values of {@link OutputEvent} types. + */ +export enum OutputEventType { + OutputUpdate = 'output', + OutputComplete = 'complete', +} + +/** + * Task output of a {@link OutputCompleteEvent} + */ +export type Output = Record | undefined | unknown; + +/** + * Update (chunk) event for the {@link OutputAPI} + */ +export type OutputUpdateEvent = + InferenceTaskEventBase & { + /** + * The id of the operation, as provided as input + */ + id: TId; + /** + * The text content of the chunk + */ + content: string; + }; + +/** + * Completion (complete message) event for the {@link OutputAPI} + */ +export type OutputCompleteEvent< + TId extends string = string, + TOutput extends Output = Output +> = InferenceTaskEventBase & { + /** + * The id of the operation, as provided as input + */ + id: TId; + /** + * The task output, following the schema specified as input + */ + output: TOutput; + /** + * Potential text content provided by the LLM, + * if it was provided in addition to the tool call + */ + content: string; +}; + +/** + * Events emitted from the {@link OutputEvent}. + */ +export type OutputEvent = + | OutputUpdateEvent + | OutputCompleteEvent; diff --git a/x-pack/packages/ai-infra/inference-common/src/output/index.ts b/x-pack/packages/ai-infra/inference-common/src/output/index.ts new file mode 100644 index 0000000000000..a3039005b2f7c --- /dev/null +++ b/x-pack/packages/ai-infra/inference-common/src/output/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { + OutputAPI, + OutputOptions, + OutputCompositeResponse, + OutputResponse, + OutputStreamResponse, +} from './api'; +export { + OutputEventType, + type OutputCompleteEvent, + type OutputUpdateEvent, + type Output, + type OutputEvent, +} from './events'; +export { + isOutputCompleteEvent, + isOutputUpdateEvent, + isOutputEvent, + withoutOutputUpdateEvents, +} from './event_utils'; diff --git a/x-pack/packages/ai-infra/inference-common/tsconfig.json b/x-pack/packages/ai-infra/inference-common/tsconfig.json new file mode 100644 index 0000000000000..86d57b8d692f7 --- /dev/null +++ b/x-pack/packages/ai-infra/inference-common/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + ] +} diff --git a/x-pack/packages/kbn-ai-assistant/src/chat/chat_flyout.tsx b/x-pack/packages/kbn-ai-assistant/src/chat/chat_flyout.tsx index 1343f5ed9a4bb..320beb1ca6b05 100644 --- a/x-pack/packages/kbn-ai-assistant/src/chat/chat_flyout.tsx +++ b/x-pack/packages/kbn-ai-assistant/src/chat/chat_flyout.tsx @@ -47,6 +47,7 @@ export function ChatFlyout({ isOpen, onClose, navigateToConversation, + hideConversationList, }: { initialTitle: string; initialMessages: Message[]; @@ -54,6 +55,7 @@ export function ChatFlyout({ isOpen: boolean; onClose: () => void; navigateToConversation?: (conversationId?: string) => void; + hideConversationList?: boolean; }) { const { euiTheme } = useEuiTheme(); const breakpoint = useCurrentEuiBreakpoint(); @@ -174,84 +176,86 @@ export function ChatFlyout({ }} > - - - setConversationsExpanded(!conversationsExpanded)} - /> - - } - /> - - {conversationsExpanded ? ( - { - conversationList.deleteConversation(deletedConversationId).then(() => { - if (deletedConversationId === conversationId) { - setConversationId(undefined); - } - }); - }} - onConversationSelect={(nextConversationId) => { - setConversationId(nextConversationId); - }} - /> - ) : ( + {!hideConversationList ? ( + - { - setConversationId(undefined); - }} + className={expandButtonClassName} + color="text" + data-test-subj="observabilityAiAssistantChatFlyoutButton" + iconType={conversationsExpanded ? 'transitionLeftIn' : 'transitionLeftOut'} + onClick={() => setConversationsExpanded(!conversationsExpanded)} /> } - className={newChatButtonClassName} /> - )} - + + {conversationsExpanded ? ( + { + conversationList.deleteConversation(deletedConversationId).then(() => { + if (deletedConversationId === conversationId) { + setConversationId(undefined); + } + }); + }} + onConversationSelect={(nextConversationId) => { + setConversationId(nextConversationId); + }} + /> + ) : ( + + { + setConversationId(undefined); + }} + /> + + } + className={newChatButtonClassName} + /> + )} + + ) : null} (null); const [mode, setMode] = useState<'prompt' | 'function'>( @@ -121,16 +123,15 @@ export function PromptEditor({ setInnerMessage(undefined); setMode('prompt'); - onSendTelemetry({ type: ObservabilityAIAssistantTelemetryEventType.UserSentPromptInChat, - payload: message, + payload: { ...message, scopes }, }); } catch (_) { setInnerMessage(oldMessage); setMode(oldMessage.function_call?.name ? 'function' : 'prompt'); } - }, [addLastUsedPrompt, innerMessage, loading, onSendTelemetry, onSubmit]); + }, [addLastUsedPrompt, innerMessage, loading, onSendTelemetry, onSubmit, scopes]); // Submit on Enter useEffect(() => { diff --git a/x-pack/packages/kbn-ai-assistant/src/prompt_editor/prompt_editor_natural_language.tsx b/x-pack/packages/kbn-ai-assistant/src/prompt_editor/prompt_editor_natural_language.tsx index 0b84b504d9507..bdef8c5e3a079 100644 --- a/x-pack/packages/kbn-ai-assistant/src/prompt_editor/prompt_editor_natural_language.tsx +++ b/x-pack/packages/kbn-ai-assistant/src/prompt_editor/prompt_editor_natural_language.tsx @@ -109,6 +109,15 @@ export function PromptEditorNaturalLanguage({ } }, [handleResizeTextArea, prompt]); + useEffect(() => { + // Attach the event listener to the window to catch mouseup outside the browser window + window.addEventListener('mouseup', handleResizeTextArea); + + return () => { + window.removeEventListener('mouseup', handleResizeTextArea); + }; + }, [handleResizeTextArea]); + return ( ; @@ -81,4 +86,5 @@ export const ReadKnowledgeBaseResponse = z.object({ is_setup_in_progress: z.boolean().optional(), pipeline_exists: z.boolean().optional(), security_labs_exists: z.boolean().optional(), + user_data_exists: z.boolean().optional(), }); diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/knowledge_base/crud_kb_route.schema.yaml b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/knowledge_base/crud_kb_route.schema.yaml index a61e98602ab40..b4c16189e2387 100644 --- a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/knowledge_base/crud_kb_route.schema.yaml +++ b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/knowledge_base/crud_kb_route.schema.yaml @@ -24,6 +24,13 @@ paths: required: false schema: type: string + - name: ignoreSecurityLabs + in: query + description: Indicates whether we should or should not install Security Labs docs when setting up the Knowledge Base + required: false + schema: + type: boolean + default: false responses: 200: description: Indicates a successful call. @@ -78,6 +85,8 @@ paths: type: boolean security_labs_exists: type: boolean + user_data_exists: + type: boolean 400: description: Generic Error content: diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/knowledge_base/get_knowledge_base_indices_route.gen.ts b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/knowledge_base/get_knowledge_base_indices_route.gen.ts new file mode 100644 index 0000000000000..0e1df8bce089f --- /dev/null +++ b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/knowledge_base/get_knowledge_base_indices_route.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Get Knowledge Base Indices API endpoints + * version: 1 + */ + +import { z } from '@kbn/zod'; + +export type GetKnowledgeBaseIndicesResponse = z.infer; +export const GetKnowledgeBaseIndicesResponse = z.object({ + /** + * List of indices with at least one field of a `sematic_text` type. + */ + indices: z.array(z.string()), +}); diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/knowledge_base/get_knowledge_base_indices_route.schema.yaml b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/knowledge_base/get_knowledge_base_indices_route.schema.yaml new file mode 100644 index 0000000000000..f9dba830f9556 --- /dev/null +++ b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/knowledge_base/get_knowledge_base_indices_route.schema.yaml @@ -0,0 +1,42 @@ +openapi: 3.0.0 +info: + title: Get Knowledge Base Indices API endpoints + version: '1' +paths: + /internal/elastic_assistant/knowledge_base/_indices: + get: + x-codegen-enabled: true + x-labels: [ess, serverless] + operationId: GetKnowledgeBaseIndices + description: Gets Knowledge Base indices that have fields of a `sematic_text` type. + summary: Gets Knowledge Base indices that have fields of a `sematic_text` type. + tags: + - KnowledgeBase API + responses: + 200: + description: Indicates a successful call. + content: + application/json: + schema: + type: object + properties: + indices: + type: array + description: List of indices with at least one field of a `sematic_text` type. + items: + type: string + required: + - indices + 400: + description: Generic Error + content: + application/json: + schema: + type: object + properties: + statusCode: + type: number + error: + type: string + message: + type: string diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/api.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/api.test.tsx index 22ccd2bc0ecdf..5509f43037444 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/api.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/api.test.tsx @@ -7,7 +7,12 @@ import { HttpSetup } from '@kbn/core-http-browser'; -import { deleteKnowledgeBase, getKnowledgeBaseStatus, postKnowledgeBase } from './api'; +import { + deleteKnowledgeBase, + getKnowledgeBaseIndices, + getKnowledgeBaseStatus, + postKnowledgeBase, +} from './api'; jest.mock('@kbn/core-http-browser'); @@ -95,4 +100,29 @@ describe('API tests', () => { await expect(deleteKnowledgeBase(knowledgeBaseArgs)).resolves.toThrowError('simulated error'); }); }); + + describe('getKnowledgeBaseIndices', () => { + it('calls the knowledge base API when correct resource path', async () => { + await getKnowledgeBaseIndices({ http: mockHttp }); + + expect(mockHttp.fetch).toHaveBeenCalledWith( + '/internal/elastic_assistant/knowledge_base/_indices', + { + method: 'GET', + signal: undefined, + version: '1', + } + ); + }); + it('returns error when error is an error', async () => { + const error = 'simulated error'; + (mockHttp.fetch as jest.Mock).mockImplementation(() => { + throw new Error(error); + }); + + await expect(getKnowledgeBaseIndices({ http: mockHttp })).resolves.toThrowError( + 'simulated error' + ); + }); + }); }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/api.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/api.tsx index 4dd03a1cb2931..4db8c0787a1e1 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/api.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/api.tsx @@ -11,7 +11,9 @@ import { CreateKnowledgeBaseResponse, DeleteKnowledgeBaseRequestParams, DeleteKnowledgeBaseResponse, + ELASTIC_AI_ASSISTANT_KNOWLEDGE_BASE_INDICES_URL, ELASTIC_AI_ASSISTANT_KNOWLEDGE_BASE_URL, + GetKnowledgeBaseIndicesResponse, ReadKnowledgeBaseRequestParams, ReadKnowledgeBaseResponse, } from '@kbn/elastic-assistant-common'; @@ -108,3 +110,32 @@ export const deleteKnowledgeBase = async ({ return error as IHttpFetchError; } }; + +/** + * API call for getting indices that have fields of `semantic_text` type. + * + * @param {Object} options - The options object. + * @param {HttpSetup} options.http - HttpSetup + * @param {AbortSignal} [options.signal] - AbortSignal + * + * @returns {Promise} + */ +export const getKnowledgeBaseIndices = async ({ + http, + signal, +}: { + http: HttpSetup; + signal?: AbortSignal | undefined; +}): Promise => { + try { + const response = await http.fetch(ELASTIC_AI_ASSISTANT_KNOWLEDGE_BASE_INDICES_URL, { + method: 'GET', + signal, + version: API_VERSIONS.internal.v1, + }); + + return response as GetKnowledgeBaseIndicesResponse; + } catch (error) { + return error as IHttpFetchError; + } +}; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/entries/use_knowledge_base_entries.ts b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/entries/use_knowledge_base_entries.ts index b41119779b21d..0775ed2d27a36 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/entries/use_knowledge_base_entries.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/entries/use_knowledge_base_entries.ts @@ -24,6 +24,7 @@ export interface UseKnowledgeBaseEntriesParams { signal?: AbortSignal | undefined; toasts?: IToasts; enabled?: boolean; // For disabling if FF is off + isRefetching?: boolean; // For enabling polling } const defaultQuery: FindKnowledgeBaseEntriesRequestQuery = { @@ -56,6 +57,7 @@ export const useKnowledgeBaseEntries = ({ signal, toasts, enabled = false, + isRefetching = false, }: UseKnowledgeBaseEntriesParams) => useQuery( KNOWLEDGE_BASE_ENTRY_QUERY_KEY, @@ -73,6 +75,7 @@ export const useKnowledgeBaseEntries = ({ enabled, keepPreviousData: true, initialData: { page: 1, perPage: 100, total: 0, data: [] }, + refetchInterval: isRefetching ? 30000 : false, onError: (error: IHttpFetchError) => { if (error.name !== 'AbortError') { toasts?.addError(error, { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/use_knowledge_base_indices.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/use_knowledge_base_indices.test.tsx new file mode 100644 index 0000000000000..4f258aa3c1964 --- /dev/null +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/use_knowledge_base_indices.test.tsx @@ -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 { act, renderHook } from '@testing-library/react-hooks'; +import { + useKnowledgeBaseIndices, + UseKnowledgeBaseIndicesParams, +} from './use_knowledge_base_indices'; +import { getKnowledgeBaseIndices as _getKnowledgeBaseIndices } from './api'; + +const getKnowledgeBaseIndicesMock = _getKnowledgeBaseIndices as jest.Mock; + +jest.mock('./api', () => { + const actual = jest.requireActual('./api'); + return { + ...actual, + getKnowledgeBaseIndices: jest.fn((...args) => actual.getKnowledgeBaseIndices(...args)), + }; +}); + +jest.mock('@tanstack/react-query', () => ({ + useQuery: jest.fn().mockImplementation(async (queryKey, fn, opts) => { + try { + const res = await fn({}); + return Promise.resolve(res); + } catch (e) { + opts.onError(e); + } + }), +})); + +const indicesResponse = ['index-1', 'index-2', 'index-3']; + +const http = { + fetch: jest.fn().mockResolvedValue(indicesResponse), +}; +const toasts = { + addError: jest.fn(), +}; +const defaultProps = { http, toasts } as unknown as UseKnowledgeBaseIndicesParams; +describe('useKnowledgeBaseIndices', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should call api to get knowledge base indices', async () => { + await act(async () => { + const { waitForNextUpdate } = renderHook(() => useKnowledgeBaseIndices(defaultProps)); + await waitForNextUpdate(); + + expect(defaultProps.http.fetch).toHaveBeenCalledWith( + '/internal/elastic_assistant/knowledge_base/_indices', + { + method: 'GET', + signal: undefined, + version: '1', + } + ); + expect(toasts.addError).not.toHaveBeenCalled(); + }); + }); + + it('should return indices response', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => useKnowledgeBaseIndices(defaultProps)); + await waitForNextUpdate(); + + await expect(result.current).resolves.toStrictEqual(indicesResponse); + }); + }); + + it('should display error toast when api throws error', async () => { + getKnowledgeBaseIndicesMock.mockRejectedValue(new Error('this is an error')); + await act(async () => { + const { waitForNextUpdate } = renderHook(() => useKnowledgeBaseIndices(defaultProps)); + await waitForNextUpdate(); + + expect(toasts.addError).toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/use_knowledge_base_indices.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/use_knowledge_base_indices.tsx new file mode 100644 index 0000000000000..2b245c70754b5 --- /dev/null +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/use_knowledge_base_indices.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 type { UseQueryResult } from '@tanstack/react-query'; +import { useQuery } from '@tanstack/react-query'; +import type { HttpSetup, IHttpFetchError, ResponseErrorBody } from '@kbn/core-http-browser'; +import type { IToasts } from '@kbn/core-notifications-browser'; +import { i18n } from '@kbn/i18n'; +import { GetKnowledgeBaseIndicesResponse } from '@kbn/elastic-assistant-common'; +import { getKnowledgeBaseIndices } from './api'; + +const KNOWLEDGE_BASE_INDICES_QUERY_KEY = ['elastic-assistant', 'knowledge-base-indices']; + +export interface UseKnowledgeBaseIndicesParams { + http: HttpSetup; + toasts?: IToasts; +} + +/** + * Hook for getting indices that have fields of `semantic_text` type. + * + * @param {Object} options - The options object. + * @param {HttpSetup} options.http - HttpSetup + * @param {IToasts} [options.toasts] - IToasts + * + * @returns {useQuery} hook for getting indices that have fields of `semantic_text` type + */ +export const useKnowledgeBaseIndices = ({ + http, + toasts, +}: UseKnowledgeBaseIndicesParams): UseQueryResult< + GetKnowledgeBaseIndicesResponse, + IHttpFetchError +> => { + return useQuery( + KNOWLEDGE_BASE_INDICES_QUERY_KEY, + async ({ signal }) => { + return getKnowledgeBaseIndices({ http, signal }); + }, + { + onError: (error: IHttpFetchError) => { + if (error.name !== 'AbortError') { + toasts?.addError( + error.body && error.body.message ? new Error(error.body.message) : error, + { + title: i18n.translate('xpack.elasticAssistant.knowledgeBase.indicesError', { + defaultMessage: 'Error fetching Knowledge Base Indices', + }), + } + ); + } + }, + } + ); +}; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/use_knowledge_base_status.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/use_knowledge_base_status.test.tsx index 80ce3d27d8dcb..83073b5770ba0 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/use_knowledge_base_status.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/use_knowledge_base_status.test.tsx @@ -34,6 +34,7 @@ const statusResponse = { elser_exists: true, index_exists: true, pipeline_exists: true, + security_labs_exists: true, }; const http = { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/use_knowledge_base_status.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/use_knowledge_base_status.tsx index 75e78f2a06948..3ae89edc2a912 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/use_knowledge_base_status.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/knowledge_base/use_knowledge_base_status.tsx @@ -20,6 +20,7 @@ export interface UseKnowledgeBaseStatusParams { http: HttpSetup; resource?: string; toasts?: IToasts; + enabled: boolean; } /** @@ -36,6 +37,7 @@ export const useKnowledgeBaseStatus = ({ http, resource, toasts, + enabled, }: UseKnowledgeBaseStatusParams): UseQueryResult => { return useQuery( KNOWLEDGE_BASE_STATUS_QUERY_KEY, @@ -43,8 +45,11 @@ export const useKnowledgeBaseStatus = ({ return getKnowledgeBaseStatus({ http, resource, signal }); }, { + enabled, retry: false, keepPreviousData: true, + // Polling interval for Knowledge Base setup in progress + refetchInterval: (data) => (data?.is_setup_in_progress ? 30000 : false), // Deprecated, hoist to `queryCache` w/in `QueryClient. See: https://stackoverflow.com/a/76961109 onError: (error: IHttpFetchError) => { if (error.name !== 'AbortError') { @@ -86,12 +91,12 @@ export const useInvalidateKnowledgeBaseStatus = () => { * * @param kbStatus ReadKnowledgeBaseResponse */ -export const isKnowledgeBaseSetup = (kbStatus: ReadKnowledgeBaseResponse | undefined): boolean => { - return ( - (kbStatus?.elser_exists && - kbStatus?.security_labs_exists && - kbStatus?.index_exists && - kbStatus?.pipeline_exists) ?? - false - ); -}; +export const isKnowledgeBaseSetup = (kbStatus: ReadKnowledgeBaseResponse | undefined): boolean => + (kbStatus?.elser_exists && + kbStatus?.index_exists && + kbStatus?.pipeline_exists && + // Allows to use UI while importing Security Labs docs + (kbStatus?.security_labs_exists || + kbStatus?.is_setup_in_progress || + kbStatus?.user_data_exists)) ?? + false; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.test.tsx index 0de7adc484fc1..f72f85892d379 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.test.tsx @@ -57,6 +57,9 @@ describe('use chat send', () => { assistantTelemetry: { reportAssistantMessageSent, }, + assistantAvailability: { + isAssistantEnabled: true, + }, }); }); it('handleOnChatCleared clears the conversation', async () => { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.tsx index 4ea376518b5a7..c240d5ac6b60b 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.tsx @@ -52,12 +52,16 @@ export const useChatSend = ({ setSelectedPromptContexts, setCurrentConversation, }: UseChatSendProps): UseChatSend => { - const { assistantTelemetry, toasts } = useAssistantContext(); + const { + assistantTelemetry, + toasts, + assistantAvailability: { isAssistantEnabled }, + } = useAssistantContext(); const [userPrompt, setUserPrompt] = useState(null); const { isLoading, sendMessage, abortStream } = useSendMessage(); const { clearConversation, removeLastMessage } = useConversation(); - const { data: kbStatus } = useKnowledgeBaseStatus({ http }); + const { data: kbStatus } = useKnowledgeBaseStatus({ http, enabled: isAssistantEnabled }); const isSetupComplete = kbStatus?.elser_exists && kbStatus?.index_exists && diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx index 1ef2db7b26c03..368477455c941 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx @@ -24,7 +24,6 @@ import { Conversation } from '../assistant_context/types'; import * as all from './chat_send/use_chat_send'; import { useConversation } from './use_conversation'; import { AIConnector } from '../connectorland/connector_selector'; -import { omit } from 'lodash'; jest.mock('../connectorland/use_load_connectors'); jest.mock('../connectorland/connector_setup'); @@ -142,84 +141,6 @@ describe('Assistant', () => { }); describe('persistent storage', () => { - it('should refetchCurrentUserConversations after settings save button click', async () => { - const chatSendSpy = jest.spyOn(all, 'useChatSend'); - await renderAssistant(); - - fireEvent.click(screen.getByTestId('settings')); - - jest.mocked(useFetchCurrentUserConversations).mockReturnValue({ - data: { - ...mockData, - welcome_id: { - ...mockData.welcome_id, - apiConfig: { newProp: true }, - }, - }, - isLoading: false, - refetch: jest.fn().mockResolvedValue({ - isLoading: false, - data: { - ...mockData, - welcome_id: { - ...mockData.welcome_id, - apiConfig: { newProp: true }, - }, - }, - }), - isFetched: true, - } as unknown as DefinedUseQueryResult, unknown>); - - await act(async () => { - fireEvent.click(screen.getByTestId('save-button')); - }); - - expect(chatSendSpy).toHaveBeenLastCalledWith( - expect.objectContaining({ - currentConversation: { - apiConfig: { newProp: true }, - category: 'assistant', - id: mockData.welcome_id.id, - messages: [], - title: 'Welcome', - replacements: {}, - }, - }) - ); - }); - - it('should refetchCurrentUserConversations after settings save button click, but do not update convos when refetch returns bad results', async () => { - jest.mocked(useFetchCurrentUserConversations).mockReturnValue({ - data: mockData, - isLoading: false, - refetch: jest.fn().mockResolvedValue({ - isLoading: false, - data: omit(mockData, 'welcome_id'), - }), - isFetched: true, - } as unknown as DefinedUseQueryResult, unknown>); - const chatSendSpy = jest.spyOn(all, 'useChatSend'); - await renderAssistant(); - - fireEvent.click(screen.getByTestId('settings')); - await act(async () => { - fireEvent.click(screen.getByTestId('save-button')); - }); - - expect(chatSendSpy).toHaveBeenLastCalledWith( - expect.objectContaining({ - currentConversation: { - apiConfig: { connectorId: '123' }, - replacements: {}, - category: 'assistant', - id: mockData.welcome_id.id, - messages: [], - title: 'Welcome', - }, - }) - ); - }); - it('should delete conversation when delete button is clicked', async () => { await renderAssistant(); const deleteButton = screen.getAllByTestId('delete-option')[0]; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings.test.tsx index 3d18885902326..763a2578ee273 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings.test.tsx @@ -26,6 +26,9 @@ const mockUseAssistantContext = { }, setAllSystemPrompts: jest.fn(), setConversations: jest.fn(), + assistantAvailability: { + isAssistantEnabled: true, + }, }; jest.mock('../assistant_context', () => { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings.tsx b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings.tsx index a46ba652574f6..18bc0cbe5a384 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings.tsx @@ -44,8 +44,16 @@ interface Props { */ export const KnowledgeBaseSettings: React.FC = React.memo( ({ knowledgeBase, setUpdatedKnowledgeBaseSettings, modalMode = false }) => { - const { http, toasts } = useAssistantContext(); - const { data: kbStatus, isLoading, isFetching } = useKnowledgeBaseStatus({ http }); + const { + http, + toasts, + assistantAvailability: { isAssistantEnabled }, + } = useAssistantContext(); + const { + data: kbStatus, + isLoading, + isFetching, + } = useKnowledgeBaseStatus({ http, enabled: isAssistantEnabled }); const { mutate: setupKB, isLoading: isSettingUpKB } = useSetupKnowledgeBase({ http, toasts }); // Resource enabled state @@ -53,9 +61,9 @@ export const KnowledgeBaseSettings: React.FC = React.memo( const isSecurityLabsEnabled = kbStatus?.security_labs_exists ?? false; const isKnowledgeBaseSetup = (isElserEnabled && - isSecurityLabsEnabled && kbStatus?.index_exists && - kbStatus?.pipeline_exists) ?? + kbStatus?.pipeline_exists && + (isSecurityLabsEnabled || kbStatus?.user_data_exists)) ?? false; const isSetupInProgress = kbStatus?.is_setup_in_progress ?? false; const isSetupAvailable = kbStatus?.is_setup_available ?? false; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index.test.tsx index 4f85dae819f0f..cfc8d2d3d52f9 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index.test.tsx @@ -24,6 +24,7 @@ import { MOCK_QUICK_PROMPTS } from '../../mock/quick_prompt'; import { useAssistantContext } from '../../..'; import { I18nProvider } from '@kbn/i18n-react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { useKnowledgeBaseIndices } from '../../assistant/api/knowledge_base/use_knowledge_base_indices'; const mockContext = { basePromptContexts: MOCK_QUICK_PROMPTS, @@ -44,13 +45,13 @@ jest.mock('../../assistant/api/knowledge_base/entries/use_update_knowledge_base_ jest.mock('../../assistant/api/knowledge_base/entries/use_delete_knowledge_base_entries'); jest.mock('../../assistant/settings/use_settings_updater/use_settings_updater'); +jest.mock('../../assistant/api/knowledge_base/use_knowledge_base_indices'); jest.mock('../../assistant/api/knowledge_base/use_knowledge_base_status'); jest.mock('../../assistant/api/knowledge_base/entries/use_knowledge_base_entries'); jest.mock( '../../assistant/common/components/assistant_settings_management/flyout/use_flyout_modal_visibility' ); const mockDataViews = { - getIndices: jest.fn().mockResolvedValue([{ name: 'index-1' }, { name: 'index-2' }]), getFieldsForWildcard: jest.fn().mockResolvedValue([ { name: 'field-1', esTypes: ['semantic_text'] }, { name: 'field-2', esTypes: ['text'] }, @@ -148,6 +149,9 @@ describe('KnowledgeBaseSettingsManagement', () => { }, isFetched: true, }); + (useKnowledgeBaseIndices as jest.Mock).mockReturnValue({ + data: { indices: ['index-1', 'index-2'] }, + }); (useKnowledgeBaseEntries as jest.Mock).mockReturnValue({ data: { data: mockData }, isFetching: false, diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index.tsx index 54ea159ff0589..904ceba7a1f6f 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index.tsx @@ -74,12 +74,15 @@ interface Params { export const KnowledgeBaseSettingsManagement: React.FC = React.memo(({ dataViews }) => { const { assistantFeatures: { assistantKnowledgeBaseByDefault: enableKnowledgeBaseByDefault }, - assistantAvailability: { hasManageGlobalKnowledgeBase }, + assistantAvailability: { hasManageGlobalKnowledgeBase, isAssistantEnabled }, http, toasts, } = useAssistantContext(); const [hasPendingChanges, setHasPendingChanges] = useState(false); - const { data: kbStatus, isFetched } = useKnowledgeBaseStatus({ http }); + const { data: kbStatus, isFetched } = useKnowledgeBaseStatus({ + http, + enabled: isAssistantEnabled, + }); const isKbSetup = isKnowledgeBaseSetup(kbStatus); const [deleteKBItem, setDeleteKBItem] = useState(null); @@ -159,7 +162,8 @@ export const KnowledgeBaseSettingsManagement: React.FC = React.memo(({ d } = useKnowledgeBaseEntries({ http, toasts, - enabled: enableKnowledgeBaseByDefault, + enabled: enableKnowledgeBaseByDefault && isAssistantEnabled, + isRefetching: kbStatus?.is_setup_in_progress, }); // Flyout Save/Cancel Actions @@ -190,13 +194,15 @@ export const KnowledgeBaseSettingsManagement: React.FC = React.memo(({ d indices.push(entry.index); } }); - return dataViews.getExistingIndices(indices); + + return indices.length ? dataViews.getExistingIndices(indices) : Promise.resolve([]); }, [entries.data]); const { getColumns } = useKnowledgeBaseTable(); const columns = useMemo( () => getColumns({ + isKbSetupInProgress: kbStatus?.is_setup_in_progress ?? false, existingIndices, isDeleteEnabled: (entry: KnowledgeBaseEntryResponse) => { return ( @@ -219,7 +225,14 @@ export const KnowledgeBaseSettingsManagement: React.FC = React.memo(({ d openFlyout(); }, }), - [entries.data, existingIndices, getColumns, hasManageGlobalKnowledgeBase, openFlyout] + [ + entries.data, + existingIndices, + getColumns, + hasManageGlobalKnowledgeBase, + kbStatus?.is_setup_in_progress, + openFlyout, + ] ); // Refresh button @@ -421,6 +434,7 @@ export const KnowledgeBaseSettingsManagement: React.FC = React.memo(({ d /> ) : ( { const mockSetEntry = jest.fn(); const mockDataViews = { - getIndices: jest.fn().mockResolvedValue([{ name: 'index-1' }, { name: 'index-2' }]), getFieldsForWildcard: jest.fn().mockResolvedValue([ { name: 'field-1', esTypes: ['semantic_text'] }, { name: 'field-2', esTypes: ['text'] }, @@ -24,6 +27,9 @@ describe('IndexEntryEditor', () => { ]), getExistingIndices: jest.fn().mockResolvedValue(['index-1']), } as unknown as DataViewsContract; + const http = { + get: jest.fn(), + } as unknown as HttpSetup; const defaultProps = { dataViews: mockDataViews, @@ -37,10 +43,14 @@ describe('IndexEntryEditor', () => { queryDescription: 'Test Query Description', users: [], } as unknown as IndexEntry, + http, }; beforeEach(() => { jest.clearAllMocks(); + (useKnowledgeBaseIndices as jest.Mock).mockReturnValue({ + data: { indices: ['index-1', 'index-2'] }, + }); }); it('renders the form fields with initial values', async () => { @@ -102,7 +112,6 @@ describe('IndexEntryEditor', () => { const { getAllByTestId, getByTestId } = render(); await waitFor(() => { - expect(mockDataViews.getIndices).toHaveBeenCalled(); fireEvent.click(getByTestId('index-combobox')); fireEvent.click(getAllByTestId('comboBoxToggleListButton')[0]); fireEvent.click(getByTestId('index-2')); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.tsx b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.tsx index ff61c61ed7423..b55fb4b1b8270 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.tsx @@ -20,10 +20,13 @@ import useAsync from 'react-use/lib/useAsync'; import React, { useCallback, useMemo } from 'react'; import { IndexEntry } from '@kbn/elastic-assistant-common'; import { DataViewsContract } from '@kbn/data-views-plugin/public'; +import { HttpSetup } from '@kbn/core-http-browser'; import * as i18n from './translations'; import { isGlobalEntry } from './helpers'; +import { useKnowledgeBaseIndices } from '../../assistant/api/knowledge_base/use_knowledge_base_indices'; interface Props { + http: HttpSetup; dataViews: DataViewsContract; entry?: IndexEntry; originalEntry?: IndexEntry; @@ -32,7 +35,7 @@ interface Props { } export const IndexEntryEditor: React.FC = React.memo( - ({ dataViews, entry, setEntry, hasManageGlobalKnowledgeBase, originalEntry }) => { + ({ http, dataViews, entry, setEntry, hasManageGlobalKnowledgeBase, originalEntry }) => { const privateUsers = useMemo(() => { const originalUsers = originalEntry?.users; if (originalEntry && !isGlobalEntry(originalEntry)) { @@ -93,18 +96,16 @@ export const IndexEntryEditor: React.FC = React.memo( entry?.users?.length === 0 ? sharingOptions[1].value : sharingOptions[0].value; // Index - const indexOptions = useAsync(async () => { - const indices = await dataViews.getIndices({ - pattern: '*', - isRollupIndex: () => false, - }); - - return indices.map((index) => ({ - 'data-test-subj': index.name, - label: index.name, - value: index.name, + const { data: kbIndices } = useKnowledgeBaseIndices({ + http, + }); + const indexOptions = useMemo(() => { + return kbIndices?.indices.map((index) => ({ + 'data-test-subj': index, + label: index, + value: index, })); - }, [dataViews]); + }, [kbIndices?.indices]); const { value: isMissingIndex } = useAsync(async () => { if (!entry?.index?.length) return false; @@ -117,7 +118,7 @@ export const IndexEntryEditor: React.FC = React.memo( dataViews.getFieldsForWildcard({ pattern: entry?.index ?? '', }), - [] + [entry?.index] ); const fieldOptions = useMemo( @@ -272,6 +273,7 @@ export const IndexEntryEditor: React.FC = React.memo( fullWidth isInvalid={isMissingIndex} error={isMissingIndex && <>{i18n.MISSING_INDEX_ERROR}} + helpText={i18n.ENTRY_INDEX_NAME_INPUT_DESCRIPTION} > = React.memo( singleSelection={{ asPlainText: true }} onCreateOption={onCreateIndexOption} fullWidth - options={indexOptions.value ?? []} + options={indexOptions ?? []} selectedOptions={ entry?.index ? [ diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/translations.ts b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/translations.ts index b311f373c214b..24784586edcdf 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/translations.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/translations.ts @@ -234,6 +234,14 @@ export const ENTRY_INDEX_NAME_INPUT_LABEL = i18n.translate( } ); +export const ENTRY_INDEX_NAME_INPUT_DESCRIPTION = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryIndexNameInputDescription', + { + defaultMessage: + 'Indices will only be available to select from this drop down list if they contain a semantic_text field. Please refer to the documentation for more information on configuring an index for use as a custom knowledge source.', + } +); + export const ENTRY_FIELD_INPUT_LABEL = i18n.translate( 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryFieldInputLabel', { @@ -372,3 +380,10 @@ export const MISSING_INDEX_TOOLTIP_CONTENT = i18n.translate( 'The index assigned to this knowledge base entry is unavailable. Check the permissions on the configured index, or that the index has not been deleted. You can update the index to be used for this knowledge entry, or delete the entry entirely.', } ); + +export const SECURITY_LABS_NOT_FULLY_LOADED = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.securityLabsNotFullyLoadedTooltipContent', + { + defaultMessage: 'Security Labs content is not fully loaded. Click to reload.', + } +); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/use_knowledge_base_table.tsx b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/use_knowledge_base_table.tsx index 7180be139c286..cbdf97f116f7b 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/use_knowledge_base_table.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/use_knowledge_base_table.tsx @@ -11,6 +11,7 @@ import { EuiBasicTableColumn, EuiIcon, EuiText, + EuiLoadingSpinner, EuiToolTip, } from '@elastic/eui'; import { css } from '@emotion/react'; @@ -29,11 +30,16 @@ import * as i18n from './translations'; import { BadgesColumn } from '../../assistant/common/components/assistant_settings_management/badges'; import { useInlineActions } from '../../assistant/common/components/assistant_settings_management/inline_actions'; import { isSystemEntry } from './helpers'; +import { SetupKnowledgeBaseButton } from '../setup_knowledge_base_button'; const AuthorColumn = ({ entry }: { entry: KnowledgeBaseEntryResponse }) => { const { userProfileService } = useAssistantContext(); const userProfile = useAsync(async () => { + if (isSystemEntry(entry) || entry.createdBy === 'unknown') { + return; + } + const profile = await userProfileService?.bulkGet<{ avatar: UserProfileAvatarData }>({ uids: new Set([entry.createdBy]), dataPath: 'avatar', @@ -45,7 +51,7 @@ const AuthorColumn = ({ entry }: { entry: KnowledgeBaseEntryResponse }) => { () => userProfile?.value?.username ?? 'Unknown', [userProfile?.value?.username] ); - const userAvatar = userProfile.value?.avatar; + const userAvatar = userProfile?.value?.avatar; const badgeItem = isSystemEntry(entry) ? 'Elastic' : userName; const userImage = isSystemEntry(entry) ? ( { isEditEnabled, onDeleteActionClicked, onEditActionClicked, + isKbSetupInProgress, }: { existingIndices?: string[]; isDeleteEnabled: (entry: KnowledgeBaseEntryResponse) => boolean; isEditEnabled: (entry: KnowledgeBaseEntryResponse) => boolean; onDeleteActionClicked: (entry: KnowledgeBaseEntryResponse) => void; onEditActionClicked: (entry: KnowledgeBaseEntryResponse) => void; + isKbSetupInProgress: boolean; }): Array> => { return [ { @@ -180,11 +188,27 @@ export const useKnowledgeBaseTable = () => { { name: i18n.COLUMN_ENTRIES, render: (entry: KnowledgeBaseEntryResponse) => { - return isSystemEntry(entry) - ? entry.text - : entry.type === DocumentEntryType.value - ? '1' - : '-'; + return isSystemEntry(entry) ? ( + <> + {`${entry.text}`} + {isKbSetupInProgress ? ( + + ) : ( + + + + )} + + ) : entry.type === DocumentEntryType.value ? ( + '1' + ) : ( + '-' + ); }, }, { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/setup_knowledge_base_button.tsx b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/setup_knowledge_base_button.tsx index d697fc7120d01..41656c968d38e 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/setup_knowledge_base_button.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/setup_knowledge_base_button.tsx @@ -6,15 +6,16 @@ */ import React, { useCallback } from 'react'; -import { EuiButton, EuiButtonEmpty, EuiToolTip } from '@elastic/eui'; +import { EuiButton, EuiButtonIcon, EuiButtonEmpty, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; import { useAssistantContext } from '../..'; import { useSetupKnowledgeBase } from '../assistant/api/knowledge_base/use_setup_knowledge_base'; import { useKnowledgeBaseStatus } from '../assistant/api/knowledge_base/use_knowledge_base_status'; interface Props { - display?: 'mini'; + display?: 'mini' | 'refresh'; } /** @@ -22,9 +23,13 @@ interface Props { * */ export const SetupKnowledgeBaseButton: React.FC = React.memo(({ display }: Props) => { - const { http, toasts } = useAssistantContext(); + const { + http, + toasts, + assistantAvailability: { isAssistantEnabled }, + } = useAssistantContext(); - const { data: kbStatus } = useKnowledgeBaseStatus({ http }); + const { data: kbStatus } = useKnowledgeBaseStatus({ http, enabled: isAssistantEnabled }); const { mutate: setupKB, isLoading: isSettingUpKB } = useSetupKnowledgeBase({ http, toasts }); const isSetupInProgress = kbStatus?.is_setup_in_progress || isSettingUpKB; @@ -48,6 +53,23 @@ export const SetupKnowledgeBaseButton: React.FC = React.memo(({ display } }) : undefined; + if (display === 'refresh') { + return ( + + ); + } + return ( {display === 'mini' ? ( diff --git a/x-pack/packages/kbn-elastic-assistant/impl/tour/knowledge_base/overview.gif b/x-pack/packages/kbn-elastic-assistant/impl/tour/knowledge_base/overview.gif index 4cf07dfecd6a9..2f2d372f5497d 100644 Binary files a/x-pack/packages/kbn-elastic-assistant/impl/tour/knowledge_base/overview.gif and b/x-pack/packages/kbn-elastic-assistant/impl/tour/knowledge_base/overview.gif differ diff --git a/x-pack/packages/kbn-elastic-assistant/impl/tour/knowledge_base/video_toast.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/tour/knowledge_base/video_toast.test.tsx index 89979efd63fef..5322a34405621 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/tour/knowledge_base/video_toast.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/tour/knowledge_base/video_toast.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { VideoToast } from './video_toast'; +import { VIDEO_PAGE, VideoToast } from './video_toast'; describe('VideoToast', () => { const onCloseMock = jest.fn(); @@ -32,19 +32,13 @@ describe('VideoToast', () => { it('should open the video in a new tab when the gif is clicked', async () => { const videoGif = screen.getByTestId('video-gif'); await userEvent.click(videoGif); - expect(window.open).toHaveBeenCalledWith( - 'https://videos.elastic.co/watch/BrDaDBAAvdygvemFKNAkBW', - '_blank' - ); + expect(window.open).toHaveBeenCalledWith(VIDEO_PAGE, '_blank'); }); it('should open the video in a new tab when the "Watch overview video" button is clicked', async () => { const watchVideoButton = screen.getByRole('button', { name: 'Watch overview video' }); await userEvent.click(watchVideoButton); - expect(window.open).toHaveBeenCalledWith( - 'https://videos.elastic.co/watch/BrDaDBAAvdygvemFKNAkBW', - '_blank' - ); + expect(window.open).toHaveBeenCalledWith(VIDEO_PAGE, '_blank'); }); it('should call the onClose callback when the close button is clicked', async () => { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/tour/knowledge_base/video_toast.tsx b/x-pack/packages/kbn-elastic-assistant/impl/tour/knowledge_base/video_toast.tsx index b1b2bfe02a1eb..8431cf687ff0c 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/tour/knowledge_base/video_toast.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/tour/knowledge_base/video_toast.tsx @@ -18,10 +18,8 @@ import React, { useCallback } from 'react'; import * as i18n from './translations'; import theGif from './overview.gif'; -const VIDEO_CONTENT_WIDTH = 250; -// TODO before removing assistantKnowledgeBaseByDefault feature flag -// update the VIDEO_PAGE to the correct URL -const VIDEO_PAGE = `https://videos.elastic.co/watch/BrDaDBAAvdygvemFKNAkBW`; +const VIDEO_CONTENT_WIDTH = 330; +export const VIDEO_PAGE = `https://ela.st/seckb`; const VideoComponent: React.FC<{ onClose: () => void }> = ({ onClose }) => { const openVideoInNewTab = useCallback(() => { diff --git a/x-pack/packages/kbn-langchain/server/language_models/mocks/index.ts b/x-pack/packages/kbn-langchain/server/language_models/mocks/index.ts index f40bafca1a469..838f93ca308af 100644 --- a/x-pack/packages/kbn-langchain/server/language_models/mocks/index.ts +++ b/x-pack/packages/kbn-langchain/server/language_models/mocks/index.ts @@ -20,6 +20,7 @@ export const mockChatCompletion: OpenAI.ChatCompletion = { message: { role: 'assistant', content: 'Yes, your name is Andrew. How can I assist you further, Andrew?', + refusal: null, }, finish_reason: 'stop', logprobs: null, diff --git a/x-pack/packages/kbn-langchain/server/language_models/types.ts b/x-pack/packages/kbn-langchain/server/language_models/types.ts index 43dcad34fda3c..35415e8eaf118 100644 --- a/x-pack/packages/kbn-langchain/server/language_models/types.ts +++ b/x-pack/packages/kbn-langchain/server/language_models/types.ts @@ -11,7 +11,10 @@ import type OpenAI from 'openai'; export interface InvokeAIActionParamsSchema { messages: Array<{ role: string; - content: string | OpenAI.ChatCompletionContentPart[]; + content: + | string + | OpenAI.ChatCompletionContentPart[] + | Array; name?: string; function_call?: { arguments: string; diff --git a/x-pack/packages/kbn-slo-schema/src/schema/slo.ts b/x-pack/packages/kbn-slo-schema/src/schema/slo.ts index dcf18d0e3a82e..0576f1cf328eb 100644 --- a/x-pack/packages/kbn-slo-schema/src/schema/slo.ts +++ b/x-pack/packages/kbn-slo-schema/src/schema/slo.ts @@ -6,6 +6,7 @@ */ import * as t from 'io-ts'; +import { Either } from 'fp-ts/Either'; import { allOrAnyStringOrArray, dateType } from './common'; import { durationType } from './duration'; import { indicatorSchema } from './indicators'; @@ -36,7 +37,35 @@ const groupBySchema = allOrAnyStringOrArray; const optionalSettingsSchema = t.partial({ ...settingsSchema.props }); const tagsSchema = t.array(t.string); -const sloIdSchema = t.string; +// id cannot contain special characters and spaces +const sloIdSchema = new t.Type( + 'sloIdSchema', + t.string.is, + (input, context): Either => { + if (typeof input === 'string') { + const valid = isValidId(input); + if (!valid) { + return t.failure( + input, + context, + 'Invalid slo id, must be between 8 and 48 characters and contain only letters, numbers, hyphens, and underscores' + ); + } + + return t.success(input); + } else { + return t.failure(input, context); + } + }, + t.identity +); + +function isValidId(id: string): boolean { + const MIN_ID_LENGTH = 8; + const MAX_ID_LENGTH = 48; + const validLength = MIN_ID_LENGTH <= id.length && id.length <= MAX_ID_LENGTH; + return validLength && /^[a-z0-9-_]+$/.test(id); +} const sloDefinitionSchema = t.type({ id: sloIdSchema, diff --git a/x-pack/packages/kbn-slo-schema/tsconfig.json b/x-pack/packages/kbn-slo-schema/tsconfig.json index bc9fd2fdede8a..cd411fff0db4a 100644 --- a/x-pack/packages/kbn-slo-schema/tsconfig.json +++ b/x-pack/packages/kbn-slo-schema/tsconfig.json @@ -12,7 +12,7 @@ ], "kbn_references": [ "@kbn/std", - "@kbn/io-ts-utils" + "@kbn/io-ts-utils", ], "exclude": [ "target/**/*", diff --git a/x-pack/packages/ml/aiops_common/constants.ts b/x-pack/packages/ml/aiops_common/constants.ts index 39a0fdc5842c8..1a75e929c147a 100644 --- a/x-pack/packages/ml/aiops_common/constants.ts +++ b/x-pack/packages/ml/aiops_common/constants.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { i18n } from '@kbn/i18n'; + /** * AIOPS_PLUGIN_ID is used as a unique identifier for the aiops plugin */ @@ -28,3 +30,14 @@ export const AIOPS_EMBEDDABLE_ORIGIN = { DISCOVER: 'discover', ML_AIOPS_LABS: 'ml_aiops_labs', } as const; + +export const AIOPS_EMBEDDABLE_GROUPING = [ + { + id: 'logs-aiops', + getDisplayName: () => + i18n.translate('xpack.aiops.embedabble.groupingDisplayName', { + defaultMessage: 'Logs AIOps', + }), + getIconType: () => 'machineLearningApp', + }, +]; diff --git a/x-pack/packages/ml/aiops_common/tsconfig.json b/x-pack/packages/ml/aiops_common/tsconfig.json index 806b5b07e847e..ffd8c074a421d 100644 --- a/x-pack/packages/ml/aiops_common/tsconfig.json +++ b/x-pack/packages/ml/aiops_common/tsconfig.json @@ -15,6 +15,7 @@ ], "kbn_references": [ "@kbn/ml-is-populated-object", + "@kbn/i18n", ], "exclude": [ "target/**/*", diff --git a/x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx b/x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx index 39291762b8fca..d9f68fe7ef890 100644 --- a/x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx +++ b/x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx @@ -426,7 +426,12 @@ export const DocumentCountChart: FC = (props) => { <> {isBrushVisible && (
-
+ {/** + * We need position:relative on this parent container of the BrushBadges, + * because of the absolute positioning of the BrushBadges. Without it, the + * BrushBadges would not be positioned correctly when used in embedded panels. + */} +
= (props) => { const d3BrushContainer = useRef(null); const brushes = useRef([]); + // id to prefix html ids for the brushes since this component can be used + // multiple times within dashboard and embedded charts. + const htmlId = useMemo(() => htmlIdGenerator()(), []); + // We need to pass props to refs here because the d3-brush code doesn't consider // native React prop changes. The brush code does its own check whether these props changed then. // The initialized brushes might otherwise act on stale data. @@ -135,10 +141,10 @@ export const DualBrush: FC = (props) => { const xMax = x(maxRef.current) ?? 0; const minExtentPx = Math.round((xMax - xMin) / 100); - const baselineBrush = d3.select('#aiops-brush-baseline'); + const baselineBrush = d3.select(`#aiops-brush-baseline-${htmlId}`); const baselineSelection = d3.brushSelection(baselineBrush.node() as SVGGElement); - const deviationBrush = d3.select('#aiops-brush-deviation'); + const deviationBrush = d3.select(`#aiops-brush-deviation-${htmlId}`); const deviationSelection = d3.brushSelection(deviationBrush.node() as SVGGElement); if (!isBrushXSelection(deviationSelection) || !isBrushXSelection(baselineSelection)) { @@ -260,7 +266,7 @@ export const DualBrush: FC = (props) => { .insert('g', '.brush') .attr('class', 'brush') .attr('id', (b: DualBrush) => { - return 'aiops-brush-' + b.id; + return `aiops-brush-${b.id}-${htmlId}`; }) .attr('data-test-subj', (b: DualBrush) => { // Uppercase the first character of the `id` so we get aiopsBrushBaseline/aiopsBrushDeviation. @@ -339,6 +345,7 @@ export const DualBrush: FC = (props) => { drawBrushes(); } }, [ + htmlId, min, max, width, diff --git a/x-pack/packages/ml/aiops_components/src/progress_controls/progress_controls.tsx b/x-pack/packages/ml/aiops_components/src/progress_controls/progress_controls.tsx index 098f7038f82c8..173f33e08f0b4 100644 --- a/x-pack/packages/ml/aiops_components/src/progress_controls/progress_controls.tsx +++ b/x-pack/packages/ml/aiops_components/src/progress_controls/progress_controls.tsx @@ -70,7 +70,6 @@ export const ProgressControls: FC> = (pr const { euiTheme } = useEuiTheme(); const runningProgressBarStyles = useAnimatedProgressBarBackground(euiTheme.colors.success); - const analysisCompleteStyle = { display: 'none' }; return ( @@ -144,32 +143,30 @@ export const ProgressControls: FC> = (pr ) : null} - - - - + + + + + + + - - - - - - + + + ) : null} {children} diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/constants.ts b/x-pack/packages/ml/aiops_log_rate_analysis/constants.ts index 054bb876a4f7a..a9812a7507441 100644 --- a/x-pack/packages/ml/aiops_log_rate_analysis/constants.ts +++ b/x-pack/packages/ml/aiops_log_rate_analysis/constants.ts @@ -33,3 +33,9 @@ export const RANDOM_SAMPLER_SEED = 3867412; /** Highlighting color for charts */ export const LOG_RATE_ANALYSIS_HIGHLIGHT_COLOR = 'orange'; + +/** */ +export const EMBEDDABLE_LOG_RATE_ANALYSIS_TYPE = 'aiopsLogRateAnalysisEmbeddable' as const; + +/** */ +export const LOG_RATE_ANALYSIS_DATA_VIEW_REF_NAME = 'aiopsLogRateAnalysisEmbeddableDataViewId'; diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/state/hooks.ts b/x-pack/packages/ml/aiops_log_rate_analysis/state/hooks.ts index 4652d604c5d61..d02a3bea22bf3 100644 --- a/x-pack/packages/ml/aiops_log_rate_analysis/state/hooks.ts +++ b/x-pack/packages/ml/aiops_log_rate_analysis/state/hooks.ts @@ -6,10 +6,9 @@ */ import type { TypedUseSelectorHook } from 'react-redux'; -import { useDispatch, useSelector, useStore } from 'react-redux'; -import type { AppDispatch, AppStore, RootState } from './store'; +import { useDispatch, useSelector } from 'react-redux'; +import type { AppDispatch, RootState } from './store'; // Improves TypeScript support compared to plain `useDispatch` and `useSelector` export const useAppDispatch: () => AppDispatch = useDispatch; export const useAppSelector: TypedUseSelectorHook = useSelector; -export const useAppStore: () => AppStore = useStore; diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/state/index.ts b/x-pack/packages/ml/aiops_log_rate_analysis/state/index.ts index 785bb02c24f31..7f7710ec23f3b 100644 --- a/x-pack/packages/ml/aiops_log_rate_analysis/state/index.ts +++ b/x-pack/packages/ml/aiops_log_rate_analysis/state/index.ts @@ -11,6 +11,7 @@ export { setAnalysisType, setAutoRunAnalysis, setDocumentCountChartData, + setGroupResults, setInitialAnalysisStart, setIsBrushCleared, setStickyHistogram, @@ -23,9 +24,10 @@ export { setPinnedSignificantItem, setSelectedGroup, setSelectedSignificantItem, -} from './log_rate_analysis_table_row_slice'; + setSkippedColumns, +} from './log_rate_analysis_table_slice'; export { LogRateAnalysisReduxProvider } from './store'; -export { useAppDispatch, useAppSelector, useAppStore } from './hooks'; +export { useAppDispatch, useAppSelector } from './hooks'; export { useCurrentSelectedGroup } from './use_current_selected_group'; export { useCurrentSelectedSignificantItem } from './use_current_selected_significant_item'; export type { GroupTableItem, GroupTableItemGroup, TableItemAction } from './types'; diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_field_candidates_slice.test.ts b/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_field_candidates_slice.test.ts index 4f829b0e0bf5a..5b4946dc2eb2b 100644 --- a/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_field_candidates_slice.test.ts +++ b/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_field_candidates_slice.test.ts @@ -9,14 +9,16 @@ import { httpServiceMock } from '@kbn/core/public/mocks'; import type { FetchFieldCandidatesResponse } from '../queries/fetch_field_candidates'; -import { fetchFieldCandidates } from './log_rate_analysis_field_candidates_slice'; +import { fetchFieldCandidates, getDefaultState } from './log_rate_analysis_field_candidates_slice'; const mockHttp = httpServiceMock.createStartContract(); describe('fetchFieldCandidates', () => { it('dispatches field candidates', async () => { const mockDispatch = jest.fn(); - const mockGetState = jest.fn(); + const mockGetState = jest.fn().mockReturnValue({ + logRateAnalysisFieldCandidates: getDefaultState(), + }); const mockResponse: FetchFieldCandidatesResponse = { isECS: false, @@ -60,7 +62,12 @@ describe('fetchFieldCandidates', () => { payload: { fieldSelectionMessage: '2 out of 5 fields were preselected for the analysis. Use the "Fields" dropdown to adjust the selection.', - fieldFilterSkippedItems: [ + initialFieldFilterSkippedItems: [ + 'another-keyword-field', + 'another-text-field', + 'yet-another-text-field', + ], + currentFieldFilterSkippedItems: [ 'another-keyword-field', 'another-text-field', 'yet-another-text-field', diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_field_candidates_slice.ts b/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_field_candidates_slice.ts index aa5cb969e5401..07b1cd6fee402 100644 --- a/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_field_candidates_slice.ts +++ b/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_field_candidates_slice.ts @@ -90,10 +90,14 @@ export const fetchFieldCandidates = createAsyncThunk( ...selectedKeywordFieldCandidates, ...selectedTextFieldCandidates, ]; - const fieldFilterSkippedItems = fieldFilterUniqueItems.filter( + const initialFieldFilterSkippedItems = fieldFilterUniqueItems.filter( (d) => !fieldFilterUniqueSelectedItems.includes(d) ); + const currentFieldFilterSkippedItems = ( + thunkApi.getState() as { logRateAnalysisFieldCandidates: FieldCandidatesState } + ).logRateAnalysisFieldCandidates.currentFieldFilterSkippedItems; + thunkApi.dispatch( setAllFieldCandidates({ fieldSelectionMessage: getFieldSelectionMessage( @@ -102,7 +106,13 @@ export const fetchFieldCandidates = createAsyncThunk( fieldFilterUniqueSelectedItems.length ), fieldFilterUniqueItems, - fieldFilterSkippedItems, + initialFieldFilterSkippedItems, + // If the currentFieldFilterSkippedItems is null, we're on the first load, + // only then we set the current skipped fields to the initial skipped fields. + currentFieldFilterSkippedItems: + currentFieldFilterSkippedItems === null + ? initialFieldFilterSkippedItems + : currentFieldFilterSkippedItems, keywordFieldCandidates, textFieldCandidates, selectedKeywordFieldCandidates, @@ -116,18 +126,20 @@ export interface FieldCandidatesState { isLoading: boolean; fieldSelectionMessage?: string; fieldFilterUniqueItems: string[]; - fieldFilterSkippedItems: string[]; + initialFieldFilterSkippedItems: string[]; + currentFieldFilterSkippedItems: string[] | null; keywordFieldCandidates: string[]; textFieldCandidates: string[]; selectedKeywordFieldCandidates: string[]; selectedTextFieldCandidates: string[]; } -function getDefaultState(): FieldCandidatesState { +export function getDefaultState(): FieldCandidatesState { return { isLoading: false, fieldFilterUniqueItems: [], - fieldFilterSkippedItems: [], + initialFieldFilterSkippedItems: [], + currentFieldFilterSkippedItems: null, keywordFieldCandidates: [], textFieldCandidates: [], selectedKeywordFieldCandidates: [], @@ -145,6 +157,12 @@ export const logRateAnalysisFieldCandidatesSlice = createSlice({ ) => { return { ...state, ...action.payload }; }, + setCurrentFieldFilterSkippedItems: ( + state: FieldCandidatesState, + action: PayloadAction + ) => { + return { ...state, currentFieldFilterSkippedItems: action.payload }; + }, }, extraReducers: (builder) => { builder.addCase(fetchFieldCandidates.pending, (state) => { @@ -157,4 +175,5 @@ export const logRateAnalysisFieldCandidatesSlice = createSlice({ }); // Action creators are generated for each case reducer function -export const { setAllFieldCandidates } = logRateAnalysisFieldCandidatesSlice.actions; +export const { setAllFieldCandidates, setCurrentFieldFilterSkippedItems } = + logRateAnalysisFieldCandidatesSlice.actions; diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_slice.ts b/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_slice.ts index 251f0d3263800..8399e896900c6 100644 --- a/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_slice.ts +++ b/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_slice.ts @@ -34,6 +34,7 @@ export interface LogRateAnalysisState { autoRunAnalysis: boolean; initialAnalysisStart: InitialAnalysisStart; isBrushCleared: boolean; + groupResults: boolean; stickyHistogram: boolean; chartWindowParameters?: WindowParameters; earliest?: number; @@ -48,6 +49,7 @@ function getDefaultState(): LogRateAnalysisState { autoRunAnalysis: true, initialAnalysisStart: undefined, isBrushCleared: true, + groupResults: false, documentStats: { sampleProbability: 1, totalCount: 0, @@ -98,6 +100,9 @@ export const logRateAnalysisSlice = createSlice({ state.intervalMs = action.payload.intervalMs; state.documentStats = action.payload.documentStats; }, + setGroupResults: (state: LogRateAnalysisState, action: PayloadAction) => { + state.groupResults = action.payload; + }, setInitialAnalysisStart: ( state: LogRateAnalysisState, action: PayloadAction @@ -127,6 +132,7 @@ export const { setAnalysisType, setAutoRunAnalysis, setDocumentCountChartData, + setGroupResults, setInitialAnalysisStart, setIsBrushCleared, setStickyHistogram, diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_table_row_slice.ts b/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_table_row_slice.ts deleted file mode 100644 index 3da98e4cc80ff..0000000000000 --- a/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_table_row_slice.ts +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { PayloadAction } from '@reduxjs/toolkit'; -import { createSlice } from '@reduxjs/toolkit'; - -import type { SignificantItem } from '@kbn/ml-agg-utils'; - -import type { GroupTableItem } from './types'; - -type SignificantItemOrNull = SignificantItem | null; -type GroupOrNull = GroupTableItem | null; - -export interface LogRateAnalysisTableRowState { - pinnedGroup: GroupOrNull; - pinnedSignificantItem: SignificantItemOrNull; - selectedGroup: GroupOrNull; - selectedSignificantItem: SignificantItemOrNull; -} - -function getDefaultState(): LogRateAnalysisTableRowState { - return { - pinnedGroup: null, - pinnedSignificantItem: null, - selectedGroup: null, - selectedSignificantItem: null, - }; -} - -export const logRateAnalysisTableRowSlice = createSlice({ - name: 'logRateAnalysisTableRow', - initialState: getDefaultState(), - reducers: { - clearAllRowState: (state: LogRateAnalysisTableRowState) => { - state.pinnedGroup = null; - state.pinnedSignificantItem = null; - state.selectedGroup = null; - state.selectedSignificantItem = null; - }, - setPinnedGroup: (state: LogRateAnalysisTableRowState, action: PayloadAction) => { - state.pinnedGroup = action.payload; - }, - setPinnedSignificantItem: ( - state: LogRateAnalysisTableRowState, - action: PayloadAction - ) => { - state.pinnedSignificantItem = action.payload; - }, - setSelectedGroup: (state: LogRateAnalysisTableRowState, action: PayloadAction) => { - state.selectedGroup = action.payload; - }, - setSelectedSignificantItem: ( - state: LogRateAnalysisTableRowState, - action: PayloadAction - ) => { - state.selectedSignificantItem = action.payload; - }, - }, -}); - -// Action creators are generated for each case reducer function -export const { - clearAllRowState, - setPinnedGroup, - setPinnedSignificantItem, - setSelectedGroup, - setSelectedSignificantItem, -} = logRateAnalysisTableRowSlice.actions; diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_table_slice.test.ts b/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_table_slice.test.ts new file mode 100644 index 0000000000000..498ada00654f0 --- /dev/null +++ b/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_table_slice.test.ts @@ -0,0 +1,130 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { configureStore } from '@reduxjs/toolkit'; +import { + logRateAnalysisTableSlice, + localStorageListenerMiddleware, + setSkippedColumns, + getPreloadedState, + AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS, + type LogRateAnalysisResultsTableColumnName, +} from './log_rate_analysis_table_slice'; + +describe('getPreloadedState', () => { + beforeEach(() => { + localStorage.clear(); + }); + + it('should return default state when localStorage is empty', () => { + const state = getPreloadedState(); + expect(state).toEqual({ + skippedColumns: ['p-value', 'Baseline rate', 'Deviation rate'], + pinnedGroup: null, + pinnedSignificantItem: null, + selectedGroup: null, + selectedSignificantItem: null, + }); + }); + + it('should return state with skippedColumns from localStorage', () => { + const skippedColumns = ['Log rate', 'Doc count']; + localStorage.setItem(AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS, JSON.stringify(skippedColumns)); + + const state = getPreloadedState(); + expect(state.skippedColumns).toEqual(skippedColumns); + }); + + it('should return default state when localStorage contains invalid JSON', () => { + localStorage.setItem(AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS, 'invalid-json'); + + const state = getPreloadedState(); + expect(state).toEqual({ + skippedColumns: ['p-value', 'Baseline rate', 'Deviation rate'], + pinnedGroup: null, + pinnedSignificantItem: null, + selectedGroup: null, + selectedSignificantItem: null, + }); + }); + + it('should return default state when localStorage does not contain skippedColumns', () => { + localStorage.setItem('someOtherKey', JSON.stringify(['someValue'])); + + const state = getPreloadedState(); + expect(state).toEqual({ + skippedColumns: ['p-value', 'Baseline rate', 'Deviation rate'], + pinnedGroup: null, + pinnedSignificantItem: null, + selectedGroup: null, + selectedSignificantItem: null, + }); + }); +}); + +type Store = ReturnType; + +describe('localStorageListenerMiddleware', () => { + let store: Store; + + beforeEach(() => { + localStorage.clear(); + store = configureStore({ + reducer: { + logRateAnalysisTable: logRateAnalysisTableSlice.reducer, + }, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware().prepend(localStorageListenerMiddleware.middleware), + }) as Store; + }); + + it('should save skippedColumns to localStorage when setSkippedColumns is dispatched', () => { + const skippedColumns: LogRateAnalysisResultsTableColumnName[] = ['Log rate', 'Doc count']; + store.dispatch(setSkippedColumns(skippedColumns)); + + const storedSkippedColumns = localStorage.getItem(AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS); + expect(storedSkippedColumns).toEqual(JSON.stringify(skippedColumns)); + }); + + it('should handle invalid JSON in localStorage gracefully', () => { + localStorage.setItem(AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS, 'invalid-json'); + const skippedColumns: LogRateAnalysisResultsTableColumnName[] = ['Log rate', 'Doc count']; + store.dispatch(setSkippedColumns(skippedColumns)); + + const storedSkippedColumns = localStorage.getItem(AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS); + expect(storedSkippedColumns).toEqual(JSON.stringify(skippedColumns)); + }); + + it('should not overwrite other localStorage keys', () => { + const otherKey = 'someOtherKey'; + const otherValue = ['someValue']; + localStorage.setItem(otherKey, JSON.stringify(otherValue)); + + const skippedColumns: LogRateAnalysisResultsTableColumnName[] = ['Log rate', 'Doc count']; + store.dispatch(setSkippedColumns(skippedColumns)); + + const storedOtherValue = localStorage.getItem(otherKey); + expect(storedOtherValue).toEqual(JSON.stringify(otherValue)); + }); + + it('should update localStorage when skippedColumns are updated multiple times', () => { + const initialSkippedColumns: LogRateAnalysisResultsTableColumnName[] = ['Log rate']; + store.dispatch(setSkippedColumns(initialSkippedColumns)); + + let storedSkippedColumns = localStorage.getItem(AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS); + expect(storedSkippedColumns).toEqual(JSON.stringify(initialSkippedColumns)); + + const updatedSkippedColumns: LogRateAnalysisResultsTableColumnName[] = [ + 'Log rate', + 'Doc count', + ]; + store.dispatch(setSkippedColumns(updatedSkippedColumns)); + + storedSkippedColumns = localStorage.getItem(AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS); + expect(storedSkippedColumns).toEqual(JSON.stringify(updatedSkippedColumns)); + }); +}); diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_table_slice.ts b/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_table_slice.ts new file mode 100644 index 0000000000000..1d9c83dea98a6 --- /dev/null +++ b/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_table_slice.ts @@ -0,0 +1,172 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { PayloadAction } from '@reduxjs/toolkit'; +import { createSlice, createListenerMiddleware } from '@reduxjs/toolkit'; + +import { i18n } from '@kbn/i18n'; +import type { SignificantItem } from '@kbn/ml-agg-utils'; + +import type { GroupTableItem } from './types'; + +export const AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS = 'aiops.logRateAnalysisResultColumns'; + +export const commonColumns = { + ['Log rate']: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.logRateColumnTitle', { + defaultMessage: 'Log rate', + }), + ['Doc count']: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.docCountColumnTitle', { + defaultMessage: 'Doc count', + }), + ['p-value']: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.pValueColumnTitle', { + defaultMessage: 'p-value', + }), + ['Impact']: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.impactColumnTitle', { + defaultMessage: 'Impact', + }), + ['Baseline rate']: i18n.translate( + 'xpack.aiops.logRateAnalysis.resultsTable.baselineRateColumnTitle', + { + defaultMessage: 'Baseline rate', + } + ), + ['Deviation rate']: i18n.translate( + 'xpack.aiops.logRateAnalysis.resultsTable.deviationRateColumnTitle', + { + defaultMessage: 'Deviation rate', + } + ), + ['Log rate change']: i18n.translate( + 'xpack.aiops.logRateAnalysis.resultsTable.logRateChangeColumnTitle', + { + defaultMessage: 'Log rate change', + } + ), + ['Actions']: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.actionsColumnTitle', { + defaultMessage: 'Actions', + }), +}; + +export const significantItemColumns = { + ['Field name']: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.fieldNameColumnTitle', { + defaultMessage: 'Field name', + }), + ['Field value']: i18n.translate( + 'xpack.aiops.logRateAnalysis.resultsTable.fieldValueColumnTitle', + { + defaultMessage: 'Field value', + } + ), + ...commonColumns, +} as const; + +export type LogRateAnalysisResultsTableColumnName = keyof typeof significantItemColumns | 'unique'; + +type SignificantItemOrNull = SignificantItem | null; +type GroupOrNull = GroupTableItem | null; + +export interface LogRateAnalysisTableState { + skippedColumns: LogRateAnalysisResultsTableColumnName[]; + pinnedGroup: GroupOrNull; + pinnedSignificantItem: SignificantItemOrNull; + selectedGroup: GroupOrNull; + selectedSignificantItem: SignificantItemOrNull; +} + +function getDefaultState(): LogRateAnalysisTableState { + return { + skippedColumns: ['p-value', 'Baseline rate', 'Deviation rate'], + pinnedGroup: null, + pinnedSignificantItem: null, + selectedGroup: null, + selectedSignificantItem: null, + }; +} + +export function getPreloadedState(): LogRateAnalysisTableState { + const defaultState = getDefaultState(); + + const localStorageSkippedColumns = localStorage.getItem(AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS); + + if (localStorageSkippedColumns === null) { + return defaultState; + } + + try { + defaultState.skippedColumns = JSON.parse(localStorageSkippedColumns); + } catch (err) { + // eslint-disable-next-line no-console + console.warn('Failed to parse skipped columns from local storage:', err); + } + + return defaultState; +} + +export const logRateAnalysisTableSlice = createSlice({ + name: 'logRateAnalysisTable', + initialState: getDefaultState(), + reducers: { + clearAllRowState: (state: LogRateAnalysisTableState) => { + state.pinnedGroup = null; + state.pinnedSignificantItem = null; + state.selectedGroup = null; + state.selectedSignificantItem = null; + }, + setPinnedGroup: (state: LogRateAnalysisTableState, action: PayloadAction) => { + state.pinnedGroup = action.payload; + }, + setPinnedSignificantItem: ( + state: LogRateAnalysisTableState, + action: PayloadAction + ) => { + state.pinnedSignificantItem = action.payload; + }, + setSelectedGroup: (state: LogRateAnalysisTableState, action: PayloadAction) => { + state.selectedGroup = action.payload; + }, + setSelectedSignificantItem: ( + state: LogRateAnalysisTableState, + action: PayloadAction + ) => { + state.selectedSignificantItem = action.payload; + }, + setSkippedColumns: ( + state: LogRateAnalysisTableState, + action: PayloadAction + ) => { + state.skippedColumns = action.payload; + }, + }, +}); + +// Action creators are generated for each case reducer function +export const { + clearAllRowState, + setPinnedGroup, + setPinnedSignificantItem, + setSelectedGroup, + setSelectedSignificantItem, + setSkippedColumns, +} = logRateAnalysisTableSlice.actions; + +// Create listener middleware +export const localStorageListenerMiddleware = createListenerMiddleware(); + +// Add a listener to save skippedColumns to localStorage whenever it changes +localStorageListenerMiddleware.startListening({ + actionCreator: setSkippedColumns, + effect: (action, listenerApi) => { + const state = listenerApi.getState() as { logRateAnalysisTable: LogRateAnalysisTableState }; + try { + const serializedState = JSON.stringify(state.logRateAnalysisTable.skippedColumns); + localStorage.setItem(AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS, serializedState); + } catch (err) { + // eslint-disable-next-line no-console + console.warn('Failed to save state to localStorage:', err); + } + }, +}); diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/state/store.tsx b/x-pack/packages/ml/aiops_log_rate_analysis/state/store.tsx index 1589b27348d89..9fd8e8240dde3 100644 --- a/x-pack/packages/ml/aiops_log_rate_analysis/state/store.tsx +++ b/x-pack/packages/ml/aiops_log_rate_analysis/state/store.tsx @@ -15,12 +15,19 @@ import { streamSlice } from '@kbn/ml-response-stream/client'; import { logRateAnalysisResultsSlice } from '../api/stream_reducer'; import { logRateAnalysisSlice } from './log_rate_analysis_slice'; -import { logRateAnalysisTableRowSlice } from './log_rate_analysis_table_row_slice'; +import { + logRateAnalysisTableSlice, + getPreloadedState, + localStorageListenerMiddleware, +} from './log_rate_analysis_table_slice'; import { logRateAnalysisFieldCandidatesSlice } from './log_rate_analysis_field_candidates_slice'; import type { InitialAnalysisStart } from './log_rate_analysis_slice'; const getReduxStore = () => configureStore({ + preloadedState: { + logRateAnalysisTable: getPreloadedState(), + }, reducer: { // General page state logRateAnalysis: logRateAnalysisSlice.reducer, @@ -28,11 +35,13 @@ const getReduxStore = () => logRateAnalysisFieldCandidates: logRateAnalysisFieldCandidatesSlice.reducer, // Analysis results logRateAnalysisResults: logRateAnalysisResultsSlice.reducer, - // Handles running the analysis - logRateAnalysisStream: streamSlice.reducer, - // Handles hovering and pinning table rows - logRateAnalysisTableRow: logRateAnalysisTableRowSlice.reducer, + // Handles running the analysis, needs to be "stream" for the async thunk to work properly. + stream: streamSlice.reducer, + // Handles hovering and pinning table rows and column selection + logRateAnalysisTable: logRateAnalysisTableSlice.reducer, }, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware().prepend(localStorageListenerMiddleware.middleware), }); interface LogRateAnalysisReduxProviderProps { @@ -54,6 +63,6 @@ export const LogRateAnalysisReduxProvider: FC< }; // Infer the `RootState` and `AppDispatch` types from the store itself -export type AppStore = ReturnType; +type AppStore = ReturnType; export type RootState = ReturnType; export type AppDispatch = AppStore['dispatch']; diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/state/use_current_selected_group.ts b/x-pack/packages/ml/aiops_log_rate_analysis/state/use_current_selected_group.ts index 9653691d3efd4..a19bd3e18a735 100644 --- a/x-pack/packages/ml/aiops_log_rate_analysis/state/use_current_selected_group.ts +++ b/x-pack/packages/ml/aiops_log_rate_analysis/state/use_current_selected_group.ts @@ -10,8 +10,8 @@ import { createSelector } from '@reduxjs/toolkit'; import type { RootState } from './store'; import { useAppSelector } from './hooks'; -const selectSelectedGroup = (s: RootState) => s.logRateAnalysisTableRow.selectedGroup; -const selectPinnedGroup = (s: RootState) => s.logRateAnalysisTableRow.pinnedGroup; +const selectSelectedGroup = (s: RootState) => s.logRateAnalysisTable.selectedGroup; +const selectPinnedGroup = (s: RootState) => s.logRateAnalysisTable.pinnedGroup; const selectCurrentSelectedGroup = createSelector( selectSelectedGroup, selectPinnedGroup, diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/state/use_current_selected_significant_item.ts b/x-pack/packages/ml/aiops_log_rate_analysis/state/use_current_selected_significant_item.ts index d189d16fc2fa0..f7327d3033df0 100644 --- a/x-pack/packages/ml/aiops_log_rate_analysis/state/use_current_selected_significant_item.ts +++ b/x-pack/packages/ml/aiops_log_rate_analysis/state/use_current_selected_significant_item.ts @@ -11,9 +11,8 @@ import type { RootState } from './store'; import { useAppSelector } from './hooks'; const selectSelectedSignificantItem = (s: RootState) => - s.logRateAnalysisTableRow.selectedSignificantItem; -const selectPinnedSignificantItem = (s: RootState) => - s.logRateAnalysisTableRow.pinnedSignificantItem; + s.logRateAnalysisTable.selectedSignificantItem; +const selectPinnedSignificantItem = (s: RootState) => s.logRateAnalysisTable.pinnedSignificantItem; const selectCurrentSelectedSignificantItem = createSelector( selectSelectedSignificantItem, selectPinnedSignificantItem, diff --git a/x-pack/packages/ml/field_stats_flyout/field_stats_flyout_provider.tsx b/x-pack/packages/ml/field_stats_flyout/field_stats_flyout_provider.tsx index 678dec7d36f42..4e7a501140b01 100644 --- a/x-pack/packages/ml/field_stats_flyout/field_stats_flyout_provider.tsx +++ b/x-pack/packages/ml/field_stats_flyout/field_stats_flyout_provider.tsx @@ -7,9 +7,8 @@ import type { PropsWithChildren, FC } from 'react'; import React, { useCallback, useState } from 'react'; -import type { CoreStart } from '@kbn/core/public'; +import type { ThemeServiceStart } from '@kbn/core-theme-browser'; import type { DataView } from '@kbn/data-plugin/common'; -import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { FieldStatsServices } from '@kbn/unified-field-list/src/components/field_stats'; import type { TimeRange as TimeRangeMs } from '@kbn/ml-date-picker'; import type { FieldStatsProps } from '@kbn/unified-field-list/src/components/field_stats'; @@ -18,32 +17,18 @@ import { getProcessedFields } from '@kbn/ml-data-grid'; import { stringHash } from '@kbn/ml-string-hash'; import { lastValueFrom } from 'rxjs'; import { useRef } from 'react'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; import { getMergedSampleDocsForPopulatedFieldsQuery } from './populated_fields/get_merged_populated_fields_query'; import { FieldStatsFlyout } from './field_stats_flyout'; import { MLFieldStatsFlyoutContext } from './use_field_stats_flyout_context'; import { PopulatedFieldsCacheManager } from './populated_fields/populated_fields_cache_manager'; -type Services = CoreStart & { - data: DataPublicPluginStart; -}; - -function useDataSearch() { - const { data } = useKibana().services; - - if (!data) { - throw new Error('Kibana data service not available.'); - } - - return data.search; -} - /** * Props for the FieldStatsFlyoutProvider component. * * @typedef {Object} FieldStatsFlyoutProviderProps * @property dataView - The data view object. * @property fieldStatsServices - Services required for field statistics. + * @property theme - The EUI theme service. * @property [timeRangeMs] - Optional time range in milliseconds. * @property [dslQuery] - Optional DSL query for filtering field statistics. * @property [disablePopulatedFields] - Optional flag to disable populated fields. @@ -51,6 +36,7 @@ function useDataSearch() { export type FieldStatsFlyoutProviderProps = PropsWithChildren<{ dataView: DataView; fieldStatsServices: FieldStatsServices; + theme: ThemeServiceStart; timeRangeMs?: TimeRangeMs; dslQuery?: FieldStatsProps['dslQuery']; disablePopulatedFields?: boolean; @@ -79,12 +65,13 @@ export const FieldStatsFlyoutProvider: FC = (prop const { dataView, fieldStatsServices, + theme, timeRangeMs, dslQuery, disablePopulatedFields = false, children, } = props; - const search = useDataSearch(); + const { search } = fieldStatsServices.data; const [isFieldStatsFlyoutVisible, setFieldStatsIsFlyoutVisible] = useState(false); const [fieldName, setFieldName] = useState(); const [fieldValue, setFieldValue] = useState(); @@ -187,6 +174,7 @@ export const FieldStatsFlyoutProvider: FC = (prop fieldValue, timeRangeMs, populatedFields, + theme, }} > = (props) => { const { field, label, onButtonClick, disabled, isEmpty, hideTrigger } = props; - const themeVars = useThemeVars(); + const theme = useFieldStatsFlyoutThemeVars(); + const themeVars = useCurrentEuiThemeVars(theme); + const emptyFieldMessage = isEmpty ? ' ' + i18n.translate('xpack.ml.newJob.wizard.fieldContextPopover.emptyFieldInSampleDocsMsg', { diff --git a/x-pack/packages/ml/field_stats_flyout/tsconfig.json b/x-pack/packages/ml/field_stats_flyout/tsconfig.json index 0010d79432e34..df70aa27788b8 100644 --- a/x-pack/packages/ml/field_stats_flyout/tsconfig.json +++ b/x-pack/packages/ml/field_stats_flyout/tsconfig.json @@ -23,9 +23,7 @@ "@kbn/i18n", "@kbn/react-field", "@kbn/ml-anomaly-utils", - "@kbn/kibana-react-plugin", "@kbn/ml-kibana-theme", - "@kbn/core", "@kbn/ml-data-grid", "@kbn/ml-string-hash", "@kbn/ml-is-populated-object", @@ -33,5 +31,6 @@ "@kbn/ml-is-defined", "@kbn/field-types", "@kbn/ui-theme", + "@kbn/core-theme-browser", ] } diff --git a/x-pack/packages/ml/field_stats_flyout/use_field_stats_flyout_context.ts b/x-pack/packages/ml/field_stats_flyout/use_field_stats_flyout_context.ts index ec6c28873011c..121426352e6e4 100644 --- a/x-pack/packages/ml/field_stats_flyout/use_field_stats_flyout_context.ts +++ b/x-pack/packages/ml/field_stats_flyout/use_field_stats_flyout_context.ts @@ -7,6 +7,7 @@ import { createContext, useContext } from 'react'; import type { TimeRange as TimeRangeMs } from '@kbn/ml-date-picker'; +import type { ThemeServiceStart } from '@kbn/core-theme-browser'; /** * Represents the properties for the MLJobWizardFieldStatsFlyout component. @@ -21,6 +22,7 @@ interface MLJobWizardFieldStatsFlyoutProps { fieldValue?: string | number; timeRangeMs?: TimeRangeMs; populatedFields?: Set; + theme?: ThemeServiceStart; } /** @@ -34,6 +36,7 @@ export const MLFieldStatsFlyoutContext = createContext {}, timeRangeMs: undefined, populatedFields: undefined, + theme: undefined, }); /** @@ -41,5 +44,25 @@ export const MLFieldStatsFlyoutContext = createContext() { populatedFields, }; } + +export type UseFieldStatsTrigger = typeof useFieldStatsTrigger; diff --git a/x-pack/packages/observability/logs_overview/src/services/categorize_logs_service/queries.ts b/x-pack/packages/observability/logs_overview/src/services/categorize_logs_service/queries.ts index aef12da303bcc..3328bd5f8585a 100644 --- a/x-pack/packages/observability/logs_overview/src/services/categorize_logs_service/queries.ts +++ b/x-pack/packages/observability/logs_overview/src/services/categorize_logs_service/queries.ts @@ -5,7 +5,10 @@ * 2.0. */ -import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; +import { + AggregationsCategorizeTextAnalyzer, + QueryDslQueryContainer, +} from '@elastic/elasticsearch/lib/api/types'; import { calculateAuto } from '@kbn/calculate-auto'; import { RandomSamplerWrapper } from '@kbn/ml-random-sampler-utils'; import moment from 'moment'; @@ -109,9 +112,7 @@ export const createCategorizationRequestParams = ({ categorize_text: { field: messageField, size: maxCategoriesCount, - categorization_analyzer: { - tokenizer: 'standard', - }, + categorization_analyzer: categorizationAnalyzerConfig, ...(minDocsPerCategory > 0 ? { min_doc_count: minDocsPerCategory } : {}), }, aggs: { @@ -149,3 +150,38 @@ export const createCategoryQuery = }, }, }); + +// This emulates the behavior of the `ml_standard` tokenizer in the ML plugin in +// regard to the hexadecimal and numeric tokens. The other parts pertaining to +// infix punctuation and file paths are not easily emulated this way. +// https://github.com/elastic/elasticsearch/blob/becd08da24df2af93eee28053d32929298cdccbd/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/categorization/MlStandardTokenizer.java#L35-L146 +// We don't use the `ml_standard` tokenizer directly because it produces tokens +// that are different from the ones produced by the `standard` tokenizer upon +// indexing. +const categorizationAnalyzerConfig: AggregationsCategorizeTextAnalyzer = { + tokenizer: 'standard', + char_filter: [ + 'first_line_with_letters', + // This ignores tokens that are hexadecimal numbers + // @ts-expect-error the official types don't support inline char filters + { + type: 'pattern_replace', + pattern: '\\b[a-fA-F][a-fA-F0-9]+\\b', + replacement: '', + }, + // This ignore tokens that start with a digit + // @ts-expect-error the official types don't support inline char filters + { + type: 'pattern_replace', + pattern: '\\b\\d\\w*\\b', + replacement: '', + }, + ], + filter: [ + // @ts-expect-error the official types don't support inline token filters + { + type: 'limit', + max_token_count: '100', + }, + ], +}; diff --git a/x-pack/packages/observability/observability_utils/es/utils/esql_result_to_plain_objects.test.ts b/x-pack/packages/observability/observability_utils/es/utils/esql_result_to_plain_objects.test.ts new file mode 100644 index 0000000000000..4557d0ba0bdd5 --- /dev/null +++ b/x-pack/packages/observability/observability_utils/es/utils/esql_result_to_plain_objects.test.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 { ESQLSearchResponse } from '@kbn/es-types'; +import { esqlResultToPlainObjects } from './esql_result_to_plain_objects'; + +describe('esqlResultToPlainObjects', () => { + it('should return an empty array for an empty result', () => { + const result: ESQLSearchResponse = { + columns: [], + values: [], + }; + const output = esqlResultToPlainObjects(result); + expect(output).toEqual([]); + }); + + it('should return plain objects', () => { + const result: ESQLSearchResponse = { + columns: [{ name: 'name', type: 'keyword' }], + values: [['Foo Bar']], + }; + const output = esqlResultToPlainObjects(result); + expect(output).toEqual([{ name: 'Foo Bar' }]); + }); + + it('should return columns without "text" or "keyword" in their names', () => { + const result: ESQLSearchResponse = { + columns: [ + { name: 'name.text', type: 'text' }, + { name: 'age', type: 'keyword' }, + ], + values: [ + ['Foo Bar', 30], + ['Foo Qux', 25], + ], + }; + const output = esqlResultToPlainObjects(result); + expect(output).toEqual([ + { name: 'Foo Bar', age: 30 }, + { name: 'Foo Qux', age: 25 }, + ]); + }); + + it('should handle mixed columns correctly', () => { + const result: ESQLSearchResponse = { + columns: [ + { name: 'name', type: 'text' }, + { name: 'name.text', type: 'text' }, + { name: 'age', type: 'keyword' }, + ], + values: [ + ['Foo Bar', 'Foo Bar', 30], + ['Foo Qux', 'Foo Qux', 25], + ], + }; + const output = esqlResultToPlainObjects(result); + expect(output).toEqual([ + { name: 'Foo Bar', age: 30 }, + { name: 'Foo Qux', age: 25 }, + ]); + }); +}); diff --git a/x-pack/packages/observability/observability_utils/es/utils/esql_result_to_plain_objects.ts b/x-pack/packages/observability/observability_utils/es/utils/esql_result_to_plain_objects.ts index ad48bcb311b25..96049f75ef156 100644 --- a/x-pack/packages/observability/observability_utils/es/utils/esql_result_to_plain_objects.ts +++ b/x-pack/packages/observability/observability_utils/es/utils/esql_result_to_plain_objects.ts @@ -13,7 +13,17 @@ export function esqlResultToPlainObjects>( return result.values.map((row) => { return row.reduce>((acc, value, index) => { const column = result.columns[index]; - acc[column.name] = value; + + if (!column) { + return acc; + } + + // Removes the type suffix from the column name + const name = column.name.replace(/\.(text|keyword)$/, ''); + if (!acc[name]) { + acc[name] = value; + } + return acc; }, {}); }) as T[]; diff --git a/x-pack/packages/security-solution/common/kibana.jsonc b/x-pack/packages/security-solution/common/kibana.jsonc deleted file mode 100644 index 708feab435425..0000000000000 --- a/x-pack/packages/security-solution/common/kibana.jsonc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "type": "shared-browser", - "id": "@kbn/security-solution-common", - "owner": "@elastic/security-threat-hunting-investigations" -} diff --git a/x-pack/packages/security-solution/common/package.json b/x-pack/packages/security-solution/common/package.json deleted file mode 100644 index ce355e927c3df..0000000000000 --- a/x-pack/packages/security-solution/common/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "@kbn/security-solution-common", - "version": "1.0.0", - "description": "security solution common components which can be used in multiple plugins such as custom discover and timeline", - "license": "Elastic License 2.0", - "private": true, - "sideEffects": false -} \ No newline at end of file diff --git a/x-pack/packages/security-solution/common/src/cells/renderers/discover.ts b/x-pack/packages/security-solution/common/src/cells/renderers/discover.ts deleted file mode 100644 index 8d0393a3c6f2b..0000000000000 --- a/x-pack/packages/security-solution/common/src/cells/renderers/discover.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 { DataGridCellValueElementProps } from '@kbn/unified-data-table'; -import { ComponentType, PropsWithChildren } from 'react'; -import { HostCellWithFlyoutRenderer } from './host'; - -export type DiscoverCellRenderer = ComponentType>; - -const RENDERERS: Record = { - 'host.name': HostCellWithFlyoutRenderer, -}; - -interface GetRendererArgs { - fieldName: string; -} - -export const getDiscoverCellRenderer = ({ fieldName }: GetRendererArgs) => { - return RENDERERS[fieldName]; -}; diff --git a/x-pack/packages/security-solution/common/src/cells/renderers/get.ts b/x-pack/packages/security-solution/common/src/cells/renderers/get.ts deleted file mode 100644 index 3102c520e31db..0000000000000 --- a/x-pack/packages/security-solution/common/src/cells/renderers/get.ts +++ /dev/null @@ -1,9 +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 { getDiscoverCellRenderer } from './discover'; -export type { DiscoverCellRenderer } from './discover'; diff --git a/x-pack/packages/security-solution/common/src/cells/renderers/host/button.test.tsx b/x-pack/packages/security-solution/common/src/cells/renderers/host/button.test.tsx deleted file mode 100644 index d49af3ed50518..0000000000000 --- a/x-pack/packages/security-solution/common/src/cells/renderers/host/button.test.tsx +++ /dev/null @@ -1,30 +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 { HostDetailsButton } from './button'; -import { render, screen } from '@testing-library/react'; - -const onClickMock = jest.fn(); -const TestComponent = () => { - return {'Test'}; -}; - -describe('Host Button', () => { - it('should render as button with link formatting', () => { - render(); - expect(screen.getByTestId('host-details-button')).toBeVisible(); - expect(screen.getByTestId('host-details-button')).toHaveAttribute('type', 'button'); - expect(screen.getByTestId('host-details-button')).toHaveClass('euiLink'); - }); - - it('should perform onClick Correctly', () => { - render(); - screen.getByTestId('host-details-button').click(); - expect(onClickMock).toHaveBeenCalled(); - }); -}); diff --git a/x-pack/packages/security-solution/common/src/cells/renderers/host/button.tsx b/x-pack/packages/security-solution/common/src/cells/renderers/host/button.tsx deleted file mode 100644 index b478ee17bf9d4..0000000000000 --- a/x-pack/packages/security-solution/common/src/cells/renderers/host/button.tsx +++ /dev/null @@ -1,27 +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, { SyntheticEvent } from 'react'; -import { EuiLink } from '@elastic/eui'; - -interface HostDetailsButtonProps { - children?: React.ReactNode; - onClick?: (e: SyntheticEvent) => void; - title?: string; -} - -export const HostDetailsButton: React.FC = ({ - children, - onClick, - title, -}) => { - return ( - - {children} - - ); -}; diff --git a/x-pack/packages/security-solution/common/src/cells/renderers/host/with_expandable_flyout.test.tsx b/x-pack/packages/security-solution/common/src/cells/renderers/host/with_expandable_flyout.test.tsx deleted file mode 100644 index 0568c656cdbe5..0000000000000 --- a/x-pack/packages/security-solution/common/src/cells/renderers/host/with_expandable_flyout.test.tsx +++ /dev/null @@ -1,54 +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 { - HostCellWithFlyoutRenderer, - HostCellWithFlyoutRendererProps, -} from './with_expandable_flyout'; -import React from 'react'; -import { render, screen } from '@testing-library/react'; - -const renderTestComponents = (props?: Partial) => { - const finalProps: HostCellWithFlyoutRendererProps = { - rowIndex: 0, - columnId: 'test', - setCellProps: jest.fn(), - isExpandable: false, - isExpanded: true, - isDetails: false, - colIndex: 0, - fieldFormats: {} as HostCellWithFlyoutRendererProps['fieldFormats'], - dataView: {} as HostCellWithFlyoutRendererProps['dataView'], - closePopover: jest.fn(), - row: { - id: '1', - raw: { - _source: { - host: { - name: 'test-host-name', - }, - }, - }, - flattened: { - 'host.name': 'test-host-name', - }, - }, - ...props, - }; - return render(); -}; - -describe('With Expandable Flyout', () => { - it('should open Expandable Flyout on Click', () => { - renderTestComponents(); - - expect(screen.getByTestId('host-details-button')).toBeVisible(); - screen.getByTestId('host-details-button').click(); - expect(screen.getByTestId('host-name-flyout')).toBeVisible(); - expect(screen.getByText('Host Flyout Header - test-host-name')).toBeVisible(); - }); -}); diff --git a/x-pack/packages/security-solution/common/src/cells/renderers/host/with_expandable_flyout.tsx b/x-pack/packages/security-solution/common/src/cells/renderers/host/with_expandable_flyout.tsx deleted file mode 100644 index 5e48d85b0384d..0000000000000 --- a/x-pack/packages/security-solution/common/src/cells/renderers/host/with_expandable_flyout.tsx +++ /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 React, { useCallback, useMemo } from 'react'; -import { getFieldValue } from '@kbn/discover-utils'; -import type { PropsWithChildren } from 'react'; -import type { DataGridCellValueElementProps } from '@kbn/unified-data-table'; -import { - ExpandableFlyout, - type ExpandableFlyoutProps, - useExpandableFlyoutApi, - withExpandableFlyoutProvider, -} from '@kbn/expandable-flyout'; -import { HostRightPanel, HostRightPanelProps } from '../../../flyout/panels'; -import { HostDetailsButton } from './button'; - -export type HostCellWithFlyoutRendererProps = PropsWithChildren; - -const HostCellWithFlyoutRendererComp = React.memo(function HostCellWithFlyoutRendererComp( - props: HostCellWithFlyoutRendererProps -) { - const hostName = getFieldValue(props.row, 'host.name') as string; - - const { openFlyout } = useExpandableFlyoutApi(); - - const onClick = useCallback(() => { - openFlyout({ - right: { - id: `host-panel-${hostName}-${props.rowIndex}`, - params: { - hostName, - }, - } as HostRightPanelProps, - }); - }, [openFlyout, hostName, props.rowIndex]); - - const panels: ExpandableFlyoutProps['registeredPanels'] = useMemo(() => { - return [ - { - key: `host-panel-${hostName}-${props.rowIndex}`, - component: (panelProps) => { - return ; - }, - }, - ]; - }, [hostName, props.rowIndex]); - - return ( - <> - - {hostName} - - ); -}); - -export const HostCellWithFlyoutRenderer = withExpandableFlyoutProvider( - HostCellWithFlyoutRendererComp -); diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/index.ts b/x-pack/packages/security-solution/common/src/flyout/common/components/index.ts deleted file mode 100644 index 4624ae2f70c55..0000000000000 --- a/x-pack/packages/security-solution/common/src/flyout/common/components/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { FlyoutFooter } from './flyout_footer'; -export { FlyoutError } from './flyout_error'; -export { FlyoutLoading } from './flyout_loading'; -export { FlyoutNavigation } from './flyout_navigation'; -export { FlyoutTitle } from './flyout_title'; -export { FlyoutBody } from './flyout_body'; -export { FlyoutHeader } from './flyout_header'; -export { FlyoutHeaderTabs } from './flyout_header_tabs'; -export { ExpandablePanel } from './expandable_panel'; diff --git a/x-pack/packages/security-solution/common/src/flyout/index.tsx b/x-pack/packages/security-solution/common/src/flyout/index.tsx deleted file mode 100644 index e919538d497c6..0000000000000 --- a/x-pack/packages/security-solution/common/src/flyout/index.tsx +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export * from './common/components'; -export * from './common/test_ids'; -export { HostRightPanel } from './panels'; diff --git a/x-pack/packages/security-solution/common/src/flyout/panels/host/right/index.tsx b/x-pack/packages/security-solution/common/src/flyout/panels/host/right/index.tsx deleted file mode 100644 index d877695f5b170..0000000000000 --- a/x-pack/packages/security-solution/common/src/flyout/panels/host/right/index.tsx +++ /dev/null @@ -1,37 +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 { FlyoutPanelProps } from '@kbn/expandable-flyout'; -import React from 'react'; -import { - FlyoutBody, - FlyoutFooter, - FlyoutHeader, - FlyoutNavigation, -} from '../../../common/components'; -// import { getEntityTableColumns } from './columns'; -// import type { BasicEntityData, EntityTableRows } from './types'; - -export interface HostRightPanelParamProps extends Record { - hostName: string; -} - -export interface HostRightPanelProps extends FlyoutPanelProps { - key: 'host'; - params: HostRightPanelParamProps; -} - -export const HostRightPanel = (props: HostRightPanelParamProps) => { - return ( - <> - - {`Host Flyout Header - ${props.hostName}`} - {'Host Flyout'} - {'Host Flyout Footer'} - - ); -}; diff --git a/x-pack/packages/security-solution/common/tsconfig.json b/x-pack/packages/security-solution/common/tsconfig.json deleted file mode 100644 index fb0d3709961d8..0000000000000 --- a/x-pack/packages/security-solution/common/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "extends": "../../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "target/types", - "types": [ - "jest", - "node", - "react", - "@testing-library/jest-dom", - "@testing-library/react", - "@emotion/react/types/css-prop" - ] - }, - "include": [ - "**/*.ts", - "**/*.tsx" - ], - "kbn_references": [ - "@kbn/unified-data-table", - "@kbn/discover-utils", - "@kbn/expandable-flyout", - "@kbn/i18n", - "@kbn/i18n-react" - ], - "exclude": [ - "target/**/*" - ] -} diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/index.test.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/index.test.tsx index b3d296c5a30db..44f51a50cc027 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/index.test.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/index.test.tsx @@ -95,23 +95,20 @@ describe('IndicesDetails', () => { describe('tour', () => { test('it renders the tour wrapping view history button of first row of first non-empty pattern', async () => { const wrapper = await screen.findByTestId('historicalResultsTour'); - const button = within(wrapper).getByRole('button', { name: 'View history' }); - expect(button).toBeInTheDocument(); + const button = within(wrapper).getByTestId( + 'viewHistoryAction-.internal.alerts-security.alerts-default-000001' + ); expect(button).toHaveAttribute('data-tour-element', patterns[1]); - expect( - screen.getByRole('dialog', { name: 'Introducing data quality history' }) - ).toBeInTheDocument(); + expect(screen.getByTestId('historicalResultsTourPanel')).toHaveTextContent( + 'Introducing data quality history' + ); }); describe('when the tour is dismissed', () => { test('it hides the tour and persists in localStorage', async () => { - const wrapper = await screen.findByRole('dialog', { - name: 'Introducing data quality history', - }); - - const button = within(wrapper).getByRole('button', { name: 'Close' }); - + const wrapper = screen.getByTestId('historicalResultsTourPanel'); + const button = within(wrapper).getByText('Close'); await userEvent.click(button); await waitFor(() => expect(screen.queryByTestId('historicalResultsTour')).toBeNull()); @@ -127,24 +124,22 @@ describe('IndicesDetails', () => { const firstNonEmptyPatternAccordionWrapper = await screen.findByTestId( `${patterns[1]}PatternPanel` ); - const accordionToggle = within(firstNonEmptyPatternAccordionWrapper).getByRole('button', { - name: /Pass/, - }); + const accordionToggle = within(firstNonEmptyPatternAccordionWrapper).getByTestId( + 'indexResultBadge' + ); await userEvent.click(accordionToggle); const secondPatternAccordionWrapper = screen.getByTestId(`${patterns[2]}PatternPanel`); const historicalResultsWrapper = await within(secondPatternAccordionWrapper).findByTestId( 'historicalResultsTour' ); - const button = within(historicalResultsWrapper).getByRole('button', { - name: 'View history', - }); + const button = within(historicalResultsWrapper).getByTestId( + `viewHistoryAction-${patternIndexNames[patterns[2]][0]}` + ); expect(button).toHaveAttribute('data-tour-element', patterns[2]); - expect( - screen.getByRole('dialog', { name: 'Introducing data quality history' }) - ).toBeInTheDocument(); - }, 10000); + expect(screen.getByTestId('historicalResultsTourPanel')).toBeInTheDocument(); + }); }); }); }); diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/historical_results_tour/index.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/historical_results_tour/index.tsx index 5e63379d17375..c35dce5da868e 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/historical_results_tour/index.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/historical_results_tour/index.tsx @@ -67,6 +67,9 @@ export const HistoricalResultsTour: FC = ({ repositionOnScroll anchor={anchorElement} zIndex={zIndex} + panelProps={{ + 'data-test-subj': 'historicalResultsTourPanel', + }} footerAction={[ {CLOSE} diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index.test.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index.test.tsx index eb6116c3276f9..8f8ed7d702d2f 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index.test.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index.test.tsx @@ -23,6 +23,7 @@ import { ERROR_LOADING_METADATA_TITLE, LOADING_STATS } from './translations'; import { useHistoricalResults } from './hooks/use_historical_results'; import { getHistoricalResultStub } from '../../../stub/get_historical_result_stub'; import userEvent from '@testing-library/user-event'; +import { HISTORY_TAB_ID, LATEST_CHECK_TAB_ID } from './constants'; const pattern = 'auditbeat-*'; @@ -94,11 +95,10 @@ describe('pattern', () => { ); - const accordionToggle = screen.getByRole('button', { - name: 'Fail auditbeat-* hot (1) unmanaged (2) Incompatible fields 4 Indices checked 3 Indices 3 Size 17.9MB Docs 19,127', - }); - - expect(accordionToggle).toBeInTheDocument(); + const accordionToggle = screen.getByTestId('patternAccordionButton-auditbeat-*'); + expect(accordionToggle).toHaveTextContent( + 'Failauditbeat-*hot (1)unmanaged (2)Incompatible fields4Indices checked3Indices3Size17.9MBDocs19,127' + ); expect(accordionToggle).toHaveAttribute('aria-expanded', 'true'); expect(screen.getByTestId('summaryTable')).toBeInTheDocument(); }); @@ -139,9 +139,10 @@ describe('pattern', () => { ); - const accordionToggle = await screen.findByRole('button', { - name: 'auditbeat-* Incompatible fields 0 Indices checked 0 Indices 0 Size 0B Docs 0', - }); + const accordionToggle = screen.getByTestId('patternAccordionButton-auditbeat-*'); + expect(accordionToggle).toHaveTextContent( + 'auditbeat-*Incompatible fields0Indices checked0Indices0Size0BDocs0' + ); expect(onAccordionToggle).toHaveBeenCalledTimes(1); @@ -186,10 +187,7 @@ describe('pattern', () => { ); - const accordionToggle = screen.getByRole('button', { - name: 'Fail auditbeat-* hot (1) unmanaged (2) Incompatible fields 4 Indices checked 3 Indices 3 Size 17.9MB Docs 19,127', - }); - + const accordionToggle = screen.getByTestId('patternAccordionButton-auditbeat-*'); expect(onAccordionToggle).toHaveBeenCalledTimes(1); await userEvent.click(accordionToggle); @@ -234,9 +232,7 @@ describe('pattern', () => { ); - const accordionToggle = screen.getByRole('button', { - name: 'Fail auditbeat-* hot (1) unmanaged (2) Incompatible fields 4 Indices checked 3 Indices 3 Size 17.9MB Docs 19,127', - }); + const accordionToggle = screen.getByTestId('patternAccordionButton-auditbeat-*'); expect(onAccordionToggle).toHaveBeenCalledTimes(1); expect(onAccordionToggle).toHaveBeenCalledWith(pattern, true, false); @@ -484,14 +480,11 @@ describe('pattern', () => { ); - const rows = screen.getAllByRole('row'); - const firstBodyRow = within(rows[1]); - expect(screen.queryByTestId('indexCheckFlyout')).not.toBeInTheDocument(); - const checkNowButton = firstBodyRow.getByRole('button', { - name: 'Check now', - }); + const checkNowButton = screen.getByTestId( + 'checkNowAction-.ds-auditbeat-8.6.1-2023.02.07-000001' + ); await userEvent.click(checkNowButton); @@ -505,12 +498,11 @@ describe('pattern', () => { indexName, pattern, }); - expect(screen.getByTestId('indexCheckFlyout')).toBeInTheDocument(); - expect(screen.getByRole('tab', { name: 'Latest Check' })).toHaveAttribute( + expect(screen.getByTestId(`indexCheckFlyoutTab-${LATEST_CHECK_TAB_ID}`)).toHaveAttribute( 'aria-selected', 'true' ); - expect(screen.getByRole('tab', { name: 'History' })).toHaveAttribute( + expect(screen.getByTestId(`indexCheckFlyoutTab-${HISTORY_TAB_ID}`)).toHaveAttribute( 'aria-selected', 'false' ); @@ -566,15 +558,11 @@ describe('pattern', () => { ); - const rows = screen.getAllByRole('row'); - const firstBodyRow = within(rows[1]); - expect(screen.queryByTestId('indexCheckFlyout')).not.toBeInTheDocument(); - const viewHistoryButton = firstBodyRow.getByRole('button', { - name: 'View history', - }); - + const viewHistoryButton = screen.getByTestId( + 'viewHistoryAction-.ds-auditbeat-8.6.1-2023.02.07-000001' + ); await userEvent.click(viewHistoryButton); // assert @@ -583,12 +571,11 @@ describe('pattern', () => { abortController: expect.any(AbortController), indexName, }); - expect(screen.getByTestId('indexCheckFlyout')).toBeInTheDocument(); - expect(screen.getByRole('tab', { name: 'Latest Check' })).toHaveAttribute( + expect(screen.getByTestId(`indexCheckFlyoutTab-${LATEST_CHECK_TAB_ID}`)).toHaveAttribute( 'aria-selected', 'false' ); - expect(screen.getByRole('tab', { name: 'History' })).toHaveAttribute( + expect(screen.getByTestId(`indexCheckFlyoutTab-${HISTORY_TAB_ID}`)).toHaveAttribute( 'aria-selected', 'true' ); @@ -644,24 +631,21 @@ describe('pattern', () => { ); - const rows = screen.getAllByRole('row'); - const firstBodyRow = within(rows[1]); - + // assert expect(screen.queryByTestId('indexCheckFlyout')).not.toBeInTheDocument(); - const viewHistoryButton = firstBodyRow.getByRole('button', { - name: 'View history', - }); + const viewHistoryButton = screen.getByTestId( + 'viewHistoryAction-.ds-auditbeat-8.6.1-2023.02.07-000001' + ); + // act await userEvent.click(viewHistoryButton); - - const closeButton = screen.getByRole('button', { name: 'Close this dialog' }); - + const closeButton = screen.getByTestId('euiFlyoutCloseButton'); await userEvent.click(closeButton); // assert expect(screen.queryByTestId('indexCheckFlyout')).not.toBeInTheDocument(); - }, 15000); + }); }); describe('when chartSelectedIndex is set', () => { @@ -718,15 +702,15 @@ describe('pattern', () => { indexName, pattern, }); - expect(screen.getByTestId('indexCheckFlyout')).toBeInTheDocument(); - expect(screen.getByRole('tab', { name: 'Latest Check' })).toHaveAttribute( + expect(screen.getByTestId(`indexCheckFlyoutTab-${LATEST_CHECK_TAB_ID}`)).toHaveAttribute( 'aria-selected', 'true' ); - expect(screen.getByRole('tab', { name: 'History' })).toHaveAttribute( + expect(screen.getByTestId(`indexCheckFlyoutTab-${HISTORY_TAB_ID}`)).toHaveAttribute( 'aria-selected', 'false' ); + expect(screen.getByTestId('latestResults')).toBeInTheDocument(); expect(screen.queryByTestId('historicalResults')).not.toBeInTheDocument(); }); @@ -766,19 +750,13 @@ describe('pattern', () => { ); - const rows = screen.getAllByRole('row'); - // skipping the first row which is the header - const firstBodyRow = within(rows[1]); - - const tourWrapper = await firstBodyRow.findByTestId('historicalResultsTour'); + const tourWrapper = await screen.findByTestId('historicalResultsTour'); expect( - within(tourWrapper).getByRole('button', { name: 'View history' }) + within(tourWrapper).getByTestId('viewHistoryAction-.ds-auditbeat-8.6.1-2023.02.07-000001') ).toBeInTheDocument(); - expect( - screen.getByRole('dialog', { name: 'Introducing data quality history' }) - ).toBeInTheDocument(); + expect(screen.getByTestId('historicalResultsTourPanel')).toBeInTheDocument(); }); describe('when accordion is collapsed', () => { @@ -815,14 +793,11 @@ describe('pattern', () => { expect(await screen.findByTestId('historicalResultsTour')).toBeInTheDocument(); - const accordionToggle = screen.getByRole('button', { - name: 'Fail auditbeat-* hot (1) unmanaged (2) Incompatible fields 4 Indices checked 3 Indices 3 Size 17.9MB Docs 19,127', - }); - + const accordionToggle = screen.getByTestId('patternAccordionButton-auditbeat-*'); await userEvent.click(accordionToggle); expect(screen.queryByTestId('historicalResultsTour')).not.toBeInTheDocument(); - }, 10000); + }); }); describe('when the tour close button is clicked', () => { @@ -859,11 +834,8 @@ describe('pattern', () => { ); - const tourDialog = await screen.findByRole('dialog', { - name: 'Introducing data quality history', - }); - - const closeButton = within(tourDialog).getByRole('button', { name: 'Close' }); + const tourDialog = await screen.findByTestId('historicalResultsTourPanel'); + const closeButton = within(tourDialog).getByText('Close'); await userEvent.click(closeButton); @@ -905,28 +877,24 @@ describe('pattern', () => { ); - const tourDialog = await screen.findByRole('dialog', { - name: 'Introducing data quality history', - }); - - const tryItButton = within(tourDialog).getByRole('button', { name: 'Try it' }); + const tourDialog = await screen.findByTestId('historicalResultsTourPanel'); + const tryItButton = within(tourDialog).getByText('Try it'); await userEvent.click(tryItButton); expect(onDismissTour).toHaveBeenCalledTimes(1); - expect(screen.getByTestId('indexCheckFlyout')).toBeInTheDocument(); - expect(screen.getByRole('tab', { name: 'Latest Check' })).toHaveAttribute( + expect(screen.getByTestId(`indexCheckFlyoutTab-${LATEST_CHECK_TAB_ID}`)).toHaveAttribute( 'aria-selected', 'false' ); - expect(screen.getByRole('tab', { name: 'History' })).toHaveAttribute( + expect(screen.getByTestId(`indexCheckFlyoutTab-${HISTORY_TAB_ID}`)).toHaveAttribute( 'aria-selected', 'true' ); }); }); - describe('when latest latest check flyout tab is opened', () => { + describe('when latest check flyout tab is opened', () => { it('hides the tour in listview and shows in flyout', async () => { (useIlmExplain as jest.Mock).mockReturnValue({ error: null, @@ -960,41 +928,41 @@ describe('pattern', () => { ); - const rows = screen.getAllByRole('row'); - // skipping the first row which is the header - const firstBodyRow = within(rows[1]); + const summaryTableWrapper = within(screen.getByTestId('summaryTable')); - expect(await firstBodyRow.findByTestId('historicalResultsTour')).toBeInTheDocument(); expect( - screen.getByRole('dialog', { name: 'Introducing data quality history' }) + await summaryTableWrapper.findByTestId('historicalResultsTour') ).toBeInTheDocument(); + expect(screen.queryByTestId('historicalResultsTourPanel')).toBeInTheDocument(); - const checkNowButton = firstBodyRow.getByRole('button', { - name: 'Check now', - }); + const checkNowButton = summaryTableWrapper.getByTestId( + 'checkNowAction-.ds-auditbeat-8.6.1-2023.02.07-000001' + ); await userEvent.click(checkNowButton); - expect(screen.getByTestId('indexCheckFlyout')).toBeInTheDocument(); - expect(screen.getByRole('tab', { name: 'Latest Check' })).toHaveAttribute( + expect(screen.getByTestId(`indexCheckFlyoutTab-${LATEST_CHECK_TAB_ID}`)).toHaveAttribute( 'aria-selected', 'true' ); - expect(screen.getByRole('tab', { name: 'History' })).toHaveAttribute( + expect(screen.getByTestId(`indexCheckFlyoutTab-${HISTORY_TAB_ID}`)).toHaveAttribute( 'aria-selected', 'false' ); - expect(firstBodyRow.queryByTestId('historicalResultsTour')).not.toBeInTheDocument(); + expect( + summaryTableWrapper.queryByTestId('historicalResultsTour') + ).not.toBeInTheDocument(); - const tabWrapper = await screen.findByRole('tab', { name: 'History' }); - await waitFor(() => + const tabWrapper = await screen.findByTestId(`indexCheckFlyoutTab-${HISTORY_TAB_ID}`); + await waitFor(() => { expect( tabWrapper.closest('[data-test-subj="historicalResultsTour"]') - ).toBeInTheDocument() - ); + ).toBeInTheDocument(); + expect(screen.queryByTestId('historicalResultsTourPanel')).toBeInTheDocument(); + }); expect(onDismissTour).not.toHaveBeenCalled(); - }, 10000); + }); }); }); diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index.tsx index a51f521eca169..fd0100bc1192e 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index.tsx @@ -322,7 +322,9 @@ const PatternComponent: React.FC = ({ id={patternComponentAccordionId} forceState={isAccordionOpen ? 'open' : 'closed'} onToggle={handleAccordionToggle} - buttonElement="div" + buttonProps={{ + 'data-test-subj': `patternAccordionButton-${pattern}`, + }} buttonContent={ { - return TOGGLE_HISTORICAL_RESULT_CHECKED_AT(getFormattedCheckTime(checkedAt)); -}; - describe('HistoricalResultsList', () => { it('should render individual historical result accordions with result outcome text, formatted check time and amount of incompatible fields', () => { const indexName = 'test'; @@ -65,13 +60,13 @@ describe('HistoricalResultsList', () => { ); expect( - screen.getByLabelText(getAccordionToggleLabel(historicalResultFail.checkedAt)) + screen.getByTestId(`historicalResultAccordionButton-${historicalResultFail.checkedAt}`) ).toHaveTextContent( `Fail${getFormattedCheckTime(historicalResultFail.checkedAt)}1 Incompatible field` ); expect( - screen.getByLabelText(getAccordionToggleLabel(historicalResultPass.checkedAt)) + screen.getByTestId(`historicalResultAccordionButton-${historicalResultPass.checkedAt}`) ).toHaveTextContent( `Pass${getFormattedCheckTime(historicalResultPass.checkedAt)}0 Incompatible fields` ); @@ -97,9 +92,9 @@ describe('HistoricalResultsList', () => { ); - const accordionToggleButton = screen.getByRole('button', { - name: TOGGLE_HISTORICAL_RESULT_CHECKED_AT(getFormattedCheckTime(historicalResult.checkedAt)), - }); + const accordionToggleButton = screen.getByTestId( + `historicalResultAccordionButton-${historicalResult.checkedAt}` + ); expect(accordionToggleButton).toBeInTheDocument(); @@ -127,11 +122,9 @@ describe('HistoricalResultsList', () => { ); - const accordionToggleButton = screen.getByRole('button', { - name: TOGGLE_HISTORICAL_RESULT_CHECKED_AT( - getFormattedCheckTime(historicalResult.checkedAt) - ), - }); + const accordionToggleButton = screen.getByTestId( + `historicalResultAccordionButton-${historicalResult.checkedAt}` + ); expect(accordionToggleButton).toBeInTheDocument(); @@ -139,15 +132,11 @@ describe('HistoricalResultsList', () => { expect(accordionToggleButton).toHaveAttribute('aria-expanded', 'true'); - const accordionToggleDiv = screen.getByLabelText( - getAccordionToggleLabel(historicalResult.checkedAt) - ); - - expect(accordionToggleDiv).toHaveTextContent( + expect(accordionToggleButton).toHaveTextContent( `Fail${getFormattedCheckTime(historicalResult.checkedAt)}` ); - expect(accordionToggleDiv).not.toHaveTextContent('1 Incompatible field'); + expect(accordionToggleButton).not.toHaveTextContent('1 Incompatible field'); }); }); @@ -198,9 +187,9 @@ describe('HistoricalResultsList', () => { ); for (const result of results) { - const accordionToggleButton = screen.getByRole('button', { - name: TOGGLE_HISTORICAL_RESULT_CHECKED_AT(getFormattedCheckTime(result.checkedAt)), - }); + const accordionToggleButton = screen.getByTestId( + `historicalResultAccordionButton-${result.checkedAt}` + ); expect(accordionToggleButton).toBeInTheDocument(); @@ -209,9 +198,7 @@ describe('HistoricalResultsList', () => { await act(async () => userEvent.click(accordionToggleButton)); } - const allAccordionToggles = screen.getAllByRole('button', { - name: /Toggle historical result checked at/, - }); + const allAccordionToggles = screen.getAllByTestId(/historicalResultAccordionButton-.*/); for (const accordionToggleButton of allAccordionToggles) { expect(accordionToggleButton).toHaveAttribute('aria-expanded', 'true'); diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/historical_results/historical_results_list/index.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/historical_results/historical_results_list/index.tsx index cabe0b26f8bac..4032f72389d58 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/historical_results/historical_results_list/index.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/historical_results/historical_results_list/index.tsx @@ -53,11 +53,11 @@ export const HistoricalResultsListComponent: FC = ({ indexName }) => { { setAccordionState((prevState) => ({ diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/historical_results/index.test.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/historical_results/index.test.tsx index 7c0b13f094030..9a74d5ffaa3f7 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/historical_results/index.test.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/historical_results/index.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { HistoricalResults } from '.'; -import { screen, render, within, act } from '@testing-library/react'; +import { screen, render, within, act, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { @@ -16,11 +16,7 @@ import { TestHistoricalResultsProvider, } from '../../../../../mock/test_providers/test_providers'; import { getHistoricalResultStub } from '../../../../../stub/get_historical_result_stub'; -import { - ERROR_LOADING_HISTORICAL_RESULTS, - FILTER_RESULTS_BY_OUTCOME, - LOADING_HISTORICAL_RESULTS, -} from './translations'; +import { ERROR_LOADING_HISTORICAL_RESULTS, LOADING_HISTORICAL_RESULTS } from './translations'; import { generateHistoricalResultsStub } from '../../../../../stub/generate_historical_results_stub'; describe('HistoricalResults', () => { @@ -44,7 +40,7 @@ describe('HistoricalResults', () => { ); - expect(screen.getByRole('status', { name: '2 checks' })).toBeInTheDocument(); + expect(screen.getByTestId('historicalResultsTotalChecks')).toBeInTheDocument(); expect(screen.getByTestId('historicalResultsList')).toBeInTheDocument(); }); @@ -69,10 +65,8 @@ describe('HistoricalResults', () => { ); - expect( - screen.getByRole('radiogroup', { name: FILTER_RESULTS_BY_OUTCOME }) - ).toBeInTheDocument(); - const outcomeFilterAll = screen.getByRole('radio', { name: 'All' }); + expect(screen.getByTestId('historicalResultsOutcomeFilterGroup')).toBeInTheDocument(); + const outcomeFilterAll = screen.getByTestId('historicalResultsOutcomeFilterAll'); expect(outcomeFilterAll).toBeInTheDocument(); expect(outcomeFilterAll).toHaveAttribute('aria-checked', 'true'); @@ -102,7 +96,7 @@ describe('HistoricalResults', () => { ); - const outcomeFilter = screen.getByRole('radio', { name: outcome }); + const outcomeFilter = screen.getByTestId(`historicalResultsOutcomeFilter${outcome}`); await act(async () => outcomeFilter.click()); const fetchQueryOpts = { @@ -145,14 +139,15 @@ describe('HistoricalResults', () => { ); const superDatePicker = screen.getByTestId('historicalResultsDatePicker'); - expect(superDatePicker).toBeInTheDocument(); expect( - within(superDatePicker).getByRole('button', { name: 'Date quick select' }) + within(superDatePicker).getByTestId('superDatePickerToggleQuickMenuButton') ).toBeInTheDocument(); expect( - within(superDatePicker).getByRole('button', { name: 'Last 30 days' }) + within(superDatePicker).getByTestId('superDatePickerShowDatesButton') + ).toHaveTextContent('Last 30 days'); + expect( + within(superDatePicker).getByTestId('superDatePickerApplyTimeButton') ).toBeInTheDocument(); - expect(within(superDatePicker).getByRole('button', { name: 'Refresh' })).toBeInTheDocument(); }); describe('when new date is selected', () => { @@ -181,14 +176,14 @@ describe('HistoricalResults', () => { const superDatePicker = screen.getByTestId('historicalResultsDatePicker'); await act(async () => { - const dateQuickSelect = within(superDatePicker).getByRole('button', { - name: 'Date quick select', - }); + const dateQuickSelect = within(superDatePicker).getByTestId( + 'superDatePickerToggleQuickMenuButton' + ); await userEvent.click(dateQuickSelect); }); await act(async () => { - const monthToDateButton = screen.getByRole('button', { name: 'Month to date' }); + const monthToDateButton = screen.getByTestId('superDatePickerCommonlyUsed_Month_to date'); await userEvent.click(monthToDateButton); }); @@ -215,7 +210,7 @@ describe('HistoricalResults', () => { describe('by default', () => { it('should show rows per page: 10 by default', () => { const indexName = 'test'; - const results = generateHistoricalResultsStub(indexName, 20); + const results = generateHistoricalResultsStub(indexName, 11); render( @@ -235,14 +230,16 @@ describe('HistoricalResults', () => { const wrapper = screen.getByTestId('historicalResultsPagination'); - expect(within(wrapper).getByText('Rows per page: 10')).toBeInTheDocument(); + expect(within(wrapper).getByTestId('tablePaginationPopoverButton')).toHaveTextContent( + 'Rows per page: 10' + ); }); }); describe('when rows per page are clicked', () => { it('should show 10, 25, 50 rows per page options', async () => { const indexName = 'test'; - const results = generateHistoricalResultsStub(indexName, 20); + const results = generateHistoricalResultsStub(indexName, 11); render( @@ -262,18 +259,20 @@ describe('HistoricalResults', () => { const wrapper = screen.getByTestId('historicalResultsPagination'); - await act(async () => userEvent.click(within(wrapper).getByText('Rows per page: 10'))); + await act(async () => + userEvent.click(within(wrapper).getByTestId('tablePaginationPopoverButton')) + ); - expect(screen.getByText('10 rows')).toBeInTheDocument(); - expect(screen.getByText('25 rows')).toBeInTheDocument(); - expect(screen.getByText('50 rows')).toBeInTheDocument(); + expect(screen.getByTestId('tablePagination-10-rows')).toBeInTheDocument(); + expect(screen.getByTestId('tablePagination-25-rows')).toBeInTheDocument(); + expect(screen.getByTestId('tablePagination-50-rows')).toBeInTheDocument(); }); }); describe('when total results are more than or equal 1 page', () => { it('should render pagination', () => { const indexName = 'test'; - const results = generateHistoricalResultsStub(indexName, 20); + const results = generateHistoricalResultsStub(indexName, 11); render( @@ -292,14 +291,12 @@ describe('HistoricalResults', () => { ); const wrapper = screen.getByTestId('historicalResultsPagination'); - - expect(within(wrapper).getByText('Rows per page: 10')).toBeInTheDocument(); - expect(within(wrapper).getByRole('list')).toBeInTheDocument(); + expect(within(wrapper).getByTestId('historicalResultsTablePagination')).toBeInTheDocument(); }); }); describe('when total results are less than 1 page', () => { - it('should not render pagination', () => { + it('should not render pagination', async () => { const indexName = 'test'; const results = generateHistoricalResultsStub(indexName, 9); render( @@ -319,14 +316,16 @@ describe('HistoricalResults', () => { ); - expect(screen.queryByTestId('historicalResultsPagination')).not.toBeInTheDocument(); + await waitFor(() => { + expect(screen.queryByTestId('historicalResultsPagination')).not.toBeInTheDocument(); + }); }); }); describe('when new page is clicked', () => { it('should invoke fetchHistoricalResults with new from and remaining fetch query opts', async () => { const indexName = 'test'; - const results = generateHistoricalResultsStub(indexName, 20); + const results = generateHistoricalResultsStub(indexName, 11); const fetchHistoricalResults = jest.fn(); render( @@ -346,9 +345,9 @@ describe('HistoricalResults', () => { ); - const nextPageButton = screen.getByLabelText('Page 2 of 2'); - expect(nextPageButton).toHaveRole('button'); - await act(async () => nextPageButton.click()); + const wrapper = screen.getByTestId('historicalResultsPagination'); + + await act(() => userEvent.click(within(wrapper).getByTestId('pagination-button-1'))); const fetchQueryOpts = { abortController: expect.any(AbortController), @@ -370,7 +369,7 @@ describe('HistoricalResults', () => { describe('when items per page is changed', () => { it('should invoke fetchHistoricalResults with new size, from: 0 and remaining fetch query opts', async () => { const indexName = 'test'; - const results = generateHistoricalResultsStub(indexName, 20); + const results = generateHistoricalResultsStub(indexName, 11); const fetchHistoricalResults = jest.fn(); render( @@ -392,9 +391,11 @@ describe('HistoricalResults', () => { const wrapper = screen.getByTestId('historicalResultsPagination'); - await act(async () => userEvent.click(within(wrapper).getByText('Rows per page: 10'))); + await act(() => + userEvent.click(within(wrapper).getByTestId('tablePaginationPopoverButton')) + ); - await act(async () => userEvent.click(screen.getByText('25 rows'))); + await act(() => userEvent.click(screen.getByTestId('tablePagination-25-rows'))); const fetchQueryOpts = { abortController: expect.any(AbortController), diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/historical_results/index.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/historical_results/index.tsx index 66fc6b100de13..3e12768efe39d 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/historical_results/index.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/historical_results/index.tsx @@ -112,10 +112,15 @@ export const HistoricalResultsComponent: FC = ({ indexName }) => {
- + @@ -124,6 +129,7 @@ export const HistoricalResultsComponent: FC = ({ indexName }) => { @@ -132,6 +138,7 @@ export const HistoricalResultsComponent: FC = ({ indexName }) => { @@ -156,6 +163,7 @@ export const HistoricalResultsComponent: FC = ({ indexName }) => { aria-live="polite" // because it's not inferred in accessibility tree aria-label={totalChecksText} + data-test-subj="historicalResultsTotalChecks" aria-describedby={historicalResultsListId} > {totalChecksText} diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/index.test.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/index.test.tsx index e73fd4c2d610d..a41e7d67e7682 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/index.test.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/index.test.tsx @@ -20,6 +20,7 @@ import { auditbeatWithAllResults } from '../../../../mock/pattern_rollup/mock_au import { mockStats } from '../../../../mock/stats/mock_stats'; import { mockHistoricalResult } from '../../../../mock/historical_results/mock_historical_results_response'; import { getFormattedCheckTime } from './utils/get_formatted_check_time'; +import { HISTORY_TAB_ID, LATEST_CHECK_TAB_ID } from '../constants'; describe('IndexCheckFlyout', () => { beforeEach(() => { @@ -55,7 +56,7 @@ describe('IndexCheckFlyout', () => { }); it('should render heading section correctly with formatted latest check time', () => { - expect(screen.getByRole('heading', { level: 2 })).toHaveTextContent( + expect(screen.getByTestId('indexCheckFlyoutHeading')).toHaveTextContent( 'auditbeat-custom-index-1' ); expect(screen.getByTestId('latestCheckedAt')).toHaveTextContent( @@ -66,12 +67,12 @@ describe('IndexCheckFlyout', () => { }); it('should render tabs correctly, with latest check preselected', () => { - expect(screen.getByRole('tab', { name: 'Latest Check' })).toHaveAttribute( + expect(screen.getByTestId(`indexCheckFlyoutTab-${LATEST_CHECK_TAB_ID}`)).toHaveAttribute( 'aria-selected', 'true' ); - expect(screen.getByRole('tab', { name: 'Latest Check' })).not.toBeDisabled(); - expect(screen.getByRole('tab', { name: 'History' })).not.toBeDisabled(); + expect(screen.getByTestId(`indexCheckFlyoutTab-${LATEST_CHECK_TAB_ID}`)).not.toBeDisabled(); + expect(screen.getByTestId(`indexCheckFlyoutTab-${HISTORY_TAB_ID}`)).not.toBeDisabled(); }); it('should render the correct index properties panel', () => { @@ -80,7 +81,7 @@ describe('IndexCheckFlyout', () => { }); it('should render footer with check now button', () => { - expect(screen.getByRole('button', { name: 'Check now' })).toBeInTheDocument(); + expect(screen.getByTestId('indexCheckFlyoutCheckNowButton')).toBeInTheDocument(); }); }); @@ -107,7 +108,7 @@ describe('IndexCheckFlyout', () => { ); - const closeButton = screen.getByRole('button', { name: 'Close this dialog' }); + const closeButton = screen.getByTestId('euiFlyoutCloseButton'); await userEvent.click(closeButton); expect(onClose).toHaveBeenCalled(); @@ -141,7 +142,7 @@ describe('IndexCheckFlyout', () => { ); - const checkNowButton = screen.getByRole('button', { name: 'Check now' }); + const checkNowButton = screen.getByTestId('indexCheckFlyoutCheckNowButton'); await userEvent.click(checkNowButton); expect(checkIndex).toHaveBeenCalledWith({ @@ -189,16 +190,12 @@ describe('IndexCheckFlyout', () => { ); - expect(screen.getByRole('tab', { name: 'Latest Check' })).toHaveAttribute( - 'aria-selected', - 'true' - ); - expect(screen.getByRole('tab', { name: 'History' })).not.toHaveAttribute( - 'aria-selected', - 'true' - ); + const latestCheckTab = screen.getByTestId(`indexCheckFlyoutTab-${LATEST_CHECK_TAB_ID}`); + expect(latestCheckTab).toHaveAttribute('aria-selected', 'true'); + + const historyTab = screen.getByTestId(`indexCheckFlyoutTab-${HISTORY_TAB_ID}`); + expect(historyTab).toHaveAttribute('aria-selected', 'false'); - const historyTab = screen.getByRole('tab', { name: 'History' }); await userEvent.click(historyTab); expect(fetchHistoricalResults).toHaveBeenCalledWith({ @@ -206,11 +203,8 @@ describe('IndexCheckFlyout', () => { abortController: expect.any(AbortController), }); - expect(screen.getByRole('tab', { name: 'History' })).toHaveAttribute('aria-selected', 'true'); - expect(screen.getByRole('tab', { name: 'Latest Check' })).not.toHaveAttribute( - 'aria-selected', - 'true' - ); + expect(historyTab).toHaveAttribute('aria-selected', 'true'); + expect(latestCheckTab).toHaveAttribute('aria-selected', 'false'); expect(screen.getByTestId('historicalResults')).toBeInTheDocument(); }); @@ -240,17 +234,15 @@ describe('IndexCheckFlyout', () => { ); - const historyTab = screen.getByRole('tab', { name: 'History' }); - const latestCheckTab = screen.getByRole('tab', { name: 'Latest Check' }); + const historyTab = screen.getByTestId(`indexCheckFlyoutTab-${HISTORY_TAB_ID}`); + const latestCheckTab = screen.getByTestId(`indexCheckFlyoutTab-${LATEST_CHECK_TAB_ID}`); expect(historyTab).toHaveAttribute('data-tour-element', `${pattern}-history-tab`); expect(latestCheckTab).not.toHaveAttribute('data-tour-element', `${pattern}-history-tab`); await waitFor(() => expect(historyTab.closest('[data-test-subj="historicalResultsTour"]')).toBeInTheDocument() ); - expect( - screen.getByRole('dialog', { name: 'Introducing data quality history' }) - ).toBeInTheDocument(); + expect(screen.getByTestId('historicalResultsTourPanel')).toBeInTheDocument(); }); describe('when the tour close button is clicked', () => { @@ -276,11 +268,8 @@ describe('IndexCheckFlyout', () => { ); - const dialogWrapper = await screen.findByRole('dialog', { - name: 'Introducing data quality history', - }); - - const closeButton = within(dialogWrapper).getByRole('button', { name: 'Close' }); + const dialogWrapper = await screen.findByTestId('historicalResultsTourPanel'); + const closeButton = within(dialogWrapper).getByText('Close'); await userEvent.click(closeButton); expect(onDismissTour).toHaveBeenCalled(); @@ -310,15 +299,13 @@ describe('IndexCheckFlyout', () => { ); - const dialogWrapper = await screen.findByRole('dialog', { - name: 'Introducing data quality history', - }); + const dialogWrapper = await screen.findByTestId('historicalResultsTourPanel'); - const tryItButton = within(dialogWrapper).getByRole('button', { name: 'Try it' }); + const tryItButton = within(dialogWrapper).getByText('Try it'); await userEvent.click(tryItButton); expect(onDismissTour).toHaveBeenCalled(); - expect(screen.getByRole('tab', { name: 'History' })).toHaveAttribute( + expect(screen.getByTestId(`indexCheckFlyoutTab-${HISTORY_TAB_ID}`)).toHaveAttribute( 'aria-selected', 'true' ); @@ -350,7 +337,7 @@ describe('IndexCheckFlyout', () => { ); - const historyTab = screen.getByRole('tab', { name: 'History' }); + const historyTab = screen.getByTestId(`indexCheckFlyoutTab-${HISTORY_TAB_ID}`); await userEvent.click(historyTab); expect(onDismissTour).toHaveBeenCalled(); @@ -384,9 +371,7 @@ describe('IndexCheckFlyout', () => { expect(screen.queryByTestId('historicalResultsTour')).not.toBeInTheDocument() ); - expect( - screen.queryByRole('dialog', { name: 'Introducing data quality history' }) - ).not.toBeInTheDocument(); + expect(screen.queryByTestId('historicalResultsTourPanel')).not.toBeInTheDocument(); }); }); }); diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/index.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/index.tsx index b6dcf850d15b0..3bb20323ca1c4 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/index.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/index_check_flyout/index.tsx @@ -171,6 +171,7 @@ export const IndexCheckFlyoutComponent: React.FC = ({ tabs.map((tab, index) => { return ( handleTabClick(tab.id)} isSelected={tab.id === selectedTabId} key={index} @@ -199,7 +200,9 @@ export const IndexCheckFlyoutComponent: React.FC = ({ {partitionedFieldMetadata?.incompatible != null && ( )} -

{indexName}

+

+ {indexName} +

{indexResult != null && indexResult.checkedAt != null && ( @@ -236,7 +239,13 @@ export const IndexCheckFlyoutComponent: React.FC = ({ - + {CHECK_NOW} diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/summary_table/utils/columns.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/summary_table/utils/columns.tsx index 832ba71d26af8..4e9bcf641ac16 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/summary_table/utils/columns.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/pattern/summary_table/utils/columns.tsx @@ -126,6 +126,7 @@ export const getSummaryTableColumns = ({ onCheckNowAction(item.indexName)} /> @@ -141,6 +142,7 @@ export const getSummaryTableColumns = ({ onViewHistoryAction(item.indexName)} {...(isFirstIndexName && { [HISTORICAL_RESULTS_TOUR_SELECTOR_KEY]: pattern, diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/hooks/use_results_rollup/hooks/use_stored_pattern_results/index.test.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/hooks/use_results_rollup/hooks/use_stored_pattern_results/index.test.tsx new file mode 100644 index 0000000000000..d58bf3af39d58 --- /dev/null +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/hooks/use_results_rollup/hooks/use_stored_pattern_results/index.test.tsx @@ -0,0 +1,108 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks'; + +import { getHistoricalResultStub } from '../../../../stub/get_historical_result_stub'; +import { useStoredPatternResults } from '.'; + +describe('useStoredPatternResults', () => { + const httpFetch = jest.fn(); + const mockToasts = notificationServiceMock.createStartContract().toasts; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('when patterns are empty', () => { + it('should return an empty array and not call getStorageResults', () => { + const { result } = renderHook(() => useStoredPatternResults([], mockToasts, httpFetch)); + + expect(result.current).toEqual([]); + expect(httpFetch).not.toHaveBeenCalled(); + }); + }); + + describe('when patterns are provided', () => { + it('should fetch and return stored pattern results correctly', async () => { + const patterns = ['pattern1-*', 'pattern2-*']; + + httpFetch.mockImplementation((path: string) => { + if (path === '/internal/ecs_data_quality_dashboard/results_latest/pattern1-*') { + return Promise.resolve([getHistoricalResultStub('pattern1-index1')]); + } + + if (path === '/internal/ecs_data_quality_dashboard/results_latest/pattern2-*') { + return Promise.resolve([getHistoricalResultStub('pattern2-index1')]); + } + + return Promise.reject(new Error('Invalid path')); + }); + + const { result, waitFor } = renderHook(() => + useStoredPatternResults(patterns, mockToasts, httpFetch) + ); + + await waitFor(() => result.current.length > 0); + + expect(httpFetch).toHaveBeenCalledTimes(2); + + expect(httpFetch).toHaveBeenCalledWith( + '/internal/ecs_data_quality_dashboard/results_latest/pattern1-*', + { + method: 'GET', + signal: expect.any(AbortSignal), + version: '1', + } + ); + expect(httpFetch).toHaveBeenCalledWith( + '/internal/ecs_data_quality_dashboard/results_latest/pattern2-*', + { + method: 'GET', + signal: expect.any(AbortSignal), + version: '1', + } + ); + + expect(result.current).toEqual([ + { + pattern: 'pattern1-*', + results: { + 'pattern1-index1': { + docsCount: expect.any(Number), + error: null, + ilmPhase: expect.any(String), + incompatible: expect.any(Number), + indexName: 'pattern1-index1', + pattern: 'pattern1-*', + markdownComments: expect.any(Array), + sameFamily: expect.any(Number), + checkedAt: expect.any(Number), + }, + }, + }, + { + pattern: 'pattern2-*', + results: { + 'pattern2-index1': { + docsCount: expect.any(Number), + error: null, + ilmPhase: expect.any(String), + incompatible: expect.any(Number), + indexName: 'pattern2-index1', + pattern: 'pattern2-*', + markdownComments: expect.any(Array), + sameFamily: expect.any(Number), + checkedAt: expect.any(Number), + }, + }, + }, + ]); + }); + }); +}); diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/hooks/use_results_rollup/hooks/use_stored_pattern_results/index.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/hooks/use_results_rollup/hooks/use_stored_pattern_results/index.tsx new file mode 100644 index 0000000000000..17334c4b4a586 --- /dev/null +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/hooks/use_results_rollup/hooks/use_stored_pattern_results/index.tsx @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { useEffect, useState } from 'react'; +import { IToasts } from '@kbn/core-notifications-browser'; +import { HttpHandler } from '@kbn/core-http-browser'; +import { isEmpty } from 'lodash/fp'; + +import { DataQualityCheckResult } from '../../../../types'; +import { formatResultFromStorage, getStorageResults } from '../../utils/storage'; + +export const useStoredPatternResults = ( + patterns: string[], + toasts: IToasts, + httpFetch: HttpHandler +) => { + const [storedPatternResults, setStoredPatternResults] = useState< + Array<{ pattern: string; results: Record }> + >([]); + + useEffect(() => { + if (isEmpty(patterns)) { + return; + } + + const abortController = new AbortController(); + const fetchStoredPatternResults = async () => { + const requests = patterns.map((pattern) => + getStorageResults({ pattern, httpFetch, abortController, toasts }).then((results = []) => ({ + pattern, + results: Object.fromEntries( + results.map((storageResult) => [ + storageResult.indexName, + formatResultFromStorage({ storageResult, pattern }), + ]) + ), + })) + ); + + const patternResults = await Promise.all(requests); + if (patternResults?.length) { + setStoredPatternResults(patternResults); + } + }; + + fetchStoredPatternResults(); + }, [httpFetch, patterns, toasts]); + + return storedPatternResults; +}; diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/hooks/use_results_rollup/index.test.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/hooks/use_results_rollup/index.test.tsx new file mode 100644 index 0000000000000..bff3c3dd54f12 --- /dev/null +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/hooks/use_results_rollup/index.test.tsx @@ -0,0 +1,685 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// fixing timezone for Date +// so when tests are run in different timezones, the results are consistent +process.env.TZ = 'UTC'; + +import { renderHook, act } from '@testing-library/react-hooks'; +import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks'; + +import type { TelemetryEvents } from '../../types'; +import { useStoredPatternResults } from './hooks/use_stored_pattern_results'; +import { mockPartitionedFieldMetadata } from '../../mock/partitioned_field_metadata/mock_partitioned_field_metadata'; +import { useResultsRollup } from '.'; +import { getPatternRollupStub } from '../../stub/get_pattern_rollup_stub'; +import { formatBytes, formatNumber } from '../../mock/test_providers/utils/format'; + +jest.mock('./hooks/use_stored_pattern_results', () => ({ + ...jest.requireActual('./hooks/use_stored_pattern_results'), + useStoredPatternResults: jest.fn().mockReturnValue([]), +})); + +describe('useResultsRollup', () => { + const httpFetch = jest.fn(); + const toasts = notificationServiceMock.createStartContract().toasts; + + const mockTelemetryEvents: TelemetryEvents = { + reportDataQualityIndexChecked: jest.fn(), + reportDataQualityCheckAllCompleted: jest.fn(), + }; + + const patterns = ['auditbeat-*', 'packetbeat-*']; + const isILMAvailable = true; + + const useStoredPatternResultsMock = useStoredPatternResults as jest.Mock; + + beforeEach(() => { + jest.clearAllMocks(); + useStoredPatternResultsMock.mockReturnValue([]); + }); + + describe('initialization', () => { + it('should initialize with default values', () => { + const { result } = renderHook(() => + useResultsRollup({ + httpFetch, + toasts, + patterns, + isILMAvailable, + telemetryEvents: mockTelemetryEvents, + }) + ); + + expect(result.current.patternIndexNames).toEqual({}); + expect(result.current.patternRollups).toEqual({}); + expect(result.current.totalDocsCount).toBe(0); + expect(result.current.totalIncompatible).toBeUndefined(); + expect(result.current.totalIndices).toBe(0); + expect(result.current.totalIndicesChecked).toBe(0); + expect(result.current.totalSameFamily).toBeUndefined(); + expect(result.current.totalSizeInBytes).toBe(0); + }); + + it('should fetch stored pattern results and update patternRollups from it', () => { + const mockStoredResults = [ + { + pattern: 'auditbeat-*', + results: { + 'auditbeat-7.11.0-2021.01.01': { + indexName: 'auditbeat-7.11.0-2021.01.01', + pattern: 'auditbeat-*', + docsCount: 500, + incompatible: 0, + error: null, + ilmPhase: 'hot', + sameFamily: 0, + markdownComments: [], + checkedAt: Date.now(), + }, + }, + }, + ]; + + useStoredPatternResultsMock.mockReturnValue(mockStoredResults); + + const { result } = renderHook(() => + useResultsRollup({ + httpFetch, + toasts, + patterns: ['auditbeat-*'], + isILMAvailable, + telemetryEvents: mockTelemetryEvents, + }) + ); + + expect(useStoredPatternResultsMock).toHaveBeenCalledWith(['auditbeat-*'], toasts, httpFetch); + + expect(result.current.patternRollups).toEqual({ + 'auditbeat-*': { + pattern: 'auditbeat-*', + results: { + 'auditbeat-7.11.0-2021.01.01': expect.any(Object), + }, + }, + }); + }); + }); + + describe('updatePatternIndexNames', () => { + it('should update pattern index names', () => { + const { result } = renderHook(() => + useResultsRollup({ + httpFetch, + toasts, + patterns, + isILMAvailable, + telemetryEvents: mockTelemetryEvents, + }) + ); + + act(() => { + result.current.updatePatternIndexNames({ + pattern: 'packetbeat-*', + indexNames: ['packetbeat-7.10.0-2021.01.01'], + }); + }); + + expect(result.current.patternIndexNames).toEqual({ + 'packetbeat-*': ['packetbeat-7.10.0-2021.01.01'], + }); + }); + }); + + describe('updatePatternRollup', () => { + it('should update pattern rollup when called', () => { + const { result } = renderHook(() => + useResultsRollup({ + httpFetch, + toasts, + patterns, + isILMAvailable, + telemetryEvents: mockTelemetryEvents, + }) + ); + + const patternRollup = getPatternRollupStub('packetbeat-*', 1); + + expect(result.current.patternRollups).toEqual({}); + + act(() => { + result.current.updatePatternRollup(patternRollup); + }); + + expect(result.current.patternRollups).toEqual({ + 'packetbeat-*': patternRollup, + }); + }); + }); + + describe('onCheckCompleted', () => { + describe('when invoked with successful check data', () => { + beforeEach(() => { + jest.useFakeTimers(); + jest.setSystemTime(new Date('2021-10-07T00:00:00Z').getTime()); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('should update patternRollup with said data, report to telemetry and persist it in storage', () => { + const { result } = renderHook(() => + useResultsRollup({ + httpFetch, + toasts, + patterns, + isILMAvailable, + telemetryEvents: mockTelemetryEvents, + }) + ); + + const patternRollup = getPatternRollupStub('packetbeat-*', 1); + + act(() => { + result.current.updatePatternRollup(patternRollup); + }); + + expect(result.current.patternRollups['packetbeat-*'].results?.['.ds-packetbeat-1']).toEqual( + { + checkedAt: new Date('2021-10-07T00:00:00Z').getTime(), + docsCount: 1000000, + error: null, + ilmPhase: 'hot', + incompatible: 0, + indexName: '.ds-packetbeat-1', + markdownComments: ['foo', 'bar', 'baz'], + pattern: 'packetbeat-*', + sameFamily: 0, + } + ); + + jest.advanceTimersByTime(1000); + + const mockOnCheckCompletedOpts = { + batchId: 'test-batch', + checkAllStartTime: Date.now(), + error: null, + formatBytes, + formatNumber, + indexName: '.ds-packetbeat-1', + partitionedFieldMetadata: mockPartitionedFieldMetadata, + pattern: 'packetbeat-*', + requestTime: 1500, + isLastCheck: true, + isCheckAll: true, + }; + + jest.advanceTimersByTime(1000); + + act(() => { + result.current.onCheckCompleted(mockOnCheckCompletedOpts); + }); + + expect(result.current.patternRollups['packetbeat-*'].results?.['.ds-packetbeat-1']).toEqual( + { + checkedAt: new Date('2021-10-07T00:00:02Z').getTime(), + docsCount: 1000000, + error: null, + ilmPhase: 'hot', + incompatible: 3, + indexName: '.ds-packetbeat-1', + markdownComments: expect.any(Array), + pattern: 'packetbeat-*', + sameFamily: 0, + } + ); + + expect(mockTelemetryEvents.reportDataQualityIndexChecked).toHaveBeenCalledWith({ + batchId: 'test-batch', + ecsVersion: '8.11.0', + errorCount: 0, + ilmPhase: 'hot', + indexId: 'uuid-1', + indexName: '.ds-packetbeat-1', + isCheckAll: true, + numberOfCustomFields: 4, + numberOfDocuments: 1000000, + numberOfEcsFields: 2, + numberOfFields: 9, + numberOfIncompatibleFields: 3, + numberOfIndices: 1, + numberOfIndicesChecked: 1, + numberOfSameFamily: 0, + sameFamilyFields: [], + sizeInBytes: 500000000, + timeConsumedMs: 1500, + unallowedMappingFields: ['host.name', 'source.ip'], + unallowedValueFields: ['event.category'], + }); + expect(mockTelemetryEvents.reportDataQualityCheckAllCompleted).toHaveBeenCalledWith({ + batchId: 'test-batch', + ecsVersion: '8.11.0', + isCheckAll: true, + numberOfDocuments: 1000000, + numberOfIncompatibleFields: 3, + numberOfIndices: 1, + numberOfIndicesChecked: 1, + numberOfSameFamily: 0, + sizeInBytes: 500000000, + timeConsumedMs: 1000, + }); + + expect(httpFetch).toHaveBeenCalledWith('/internal/ecs_data_quality_dashboard/results', { + method: 'POST', + version: '1', + signal: expect.any(AbortSignal), + body: expect.any(String), + }); + + const body = JSON.parse(httpFetch.mock.calls[0][1].body); + + expect(body).toEqual({ + batchId: 'test-batch', + indexName: '.ds-packetbeat-1', + indexPattern: 'packetbeat-*', + isCheckAll: true, + checkedAt: new Date('2021-10-07T00:00:02Z').getTime(), + docsCount: 1000000, + totalFieldCount: 9, + ecsFieldCount: 2, + customFieldCount: 4, + incompatibleFieldCount: 3, + incompatibleFieldMappingItems: [ + { + fieldName: 'host.name', + expectedValue: 'keyword', + actualValue: 'text', + description: + 'Name of the host.\nIt can contain what `hostname` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.', + }, + { + fieldName: 'source.ip', + expectedValue: 'ip', + actualValue: 'text', + description: 'IP address of the source (IPv4 or IPv6).', + }, + ], + incompatibleFieldValueItems: [ + { + fieldName: 'event.category', + expectedValues: [ + 'authentication', + 'configuration', + 'database', + 'driver', + 'email', + 'file', + 'host', + 'iam', + 'intrusion_detection', + 'malware', + 'network', + 'package', + 'process', + 'registry', + 'session', + 'threat', + 'vulnerability', + 'web', + ], + actualValues: [ + { name: 'an_invalid_category', count: 2 }, + { name: 'theory', count: 1 }, + ], + description: + 'This is one of four ECS Categorization Fields, and indicates the second level in the ECS category hierarchy.\n`event.category` represents the "big buckets" of ECS categories. For example, filtering on `event.category:process` yields all events relating to process activity. This field is closely related to `event.type`, which is used as a subcategory.\nThis field is an array. This will allow proper categorization of some events that fall in multiple categories.', + }, + ], + sameFamilyFieldCount: 0, + sameFamilyFields: [], + sameFamilyFieldItems: [], + unallowedMappingFields: ['host.name', 'source.ip'], + unallowedValueFields: ['event.category'], + sizeInBytes: 500000000, + ilmPhase: 'hot', + markdownComments: [ + '### .ds-packetbeat-1\n', + '| Result | Index | Docs | Incompatible fields | ILM Phase | Size |\n|--------|-------|------|---------------------|-----------|------|\n| ❌ | .ds-packetbeat-1 | 1,000,000 (100.0%) | 3 | `hot` | 476.8MB |\n\n', + '### **Incompatible fields** `3` **Same family** `0` **Custom fields** `4` **ECS compliant fields** `2` **All fields** `9`\n', + "#### 3 incompatible fields\n\nFields are incompatible with ECS when index mappings, or the values of the fields in the index, don't conform to the Elastic Common Schema (ECS), version 8.11.0.\n\n❌ Detection engine rules referencing these fields may not match them correctly\n❌ Pages may not display some events or fields due to unexpected field mappings or values\n❌ Mappings or field values that don't comply with ECS are not supported\n", + '\n#### Incompatible field mappings - .ds-packetbeat-1\n\n\n| Field | ECS mapping type (expected) | Index mapping type (actual) | \n|-------|-----------------------------|-----------------------------|\n| host.name | `keyword` | `text` |\n| source.ip | `ip` | `text` |\n\n#### Incompatible field values - .ds-packetbeat-1\n\n\n| Field | ECS values (expected) | Document values (actual) | \n|-------|-----------------------|--------------------------|\n| event.category | `authentication`, `configuration`, `database`, `driver`, `email`, `file`, `host`, `iam`, `intrusion_detection`, `malware`, `network`, `package`, `process`, `registry`, `session`, `threat`, `vulnerability`, `web` | `an_invalid_category` (2), `theory` (1) |\n\n', + ], + ecsVersion: '8.11.0', + indexId: 'uuid-1', + error: null, + }); + }); + + describe('when isILMAvailable is false', () => { + it('should omit ilmPhase and nullify sizeInBytes when storing payload', () => { + const { result } = renderHook(() => + useResultsRollup({ + httpFetch, + toasts, + patterns, + isILMAvailable: false, + telemetryEvents: mockTelemetryEvents, + }) + ); + + const patternRollup = getPatternRollupStub('packetbeat-*', 1, false); + + act(() => { + result.current.updatePatternRollup(patternRollup); + }); + + jest.advanceTimersByTime(1000); + + const mockOnCheckCompletedOpts = { + batchId: 'test-batch', + checkAllStartTime: Date.now(), + error: null, + formatBytes, + formatNumber, + indexName: '.ds-packetbeat-1', + partitionedFieldMetadata: mockPartitionedFieldMetadata, + pattern: 'packetbeat-*', + requestTime: 1500, + isLastCheck: true, + isCheckAll: true, + }; + + jest.advanceTimersByTime(1000); + + act(() => { + result.current.onCheckCompleted(mockOnCheckCompletedOpts); + }); + + expect(mockTelemetryEvents.reportDataQualityIndexChecked).toHaveBeenCalledWith({ + batchId: 'test-batch', + ecsVersion: '8.11.0', + errorCount: 0, + ilmPhase: undefined, + indexId: 'uuid-1', + indexName: '.ds-packetbeat-1', + isCheckAll: true, + numberOfCustomFields: 4, + numberOfDocuments: 1000000, + numberOfEcsFields: 2, + numberOfFields: 9, + numberOfIncompatibleFields: 3, + numberOfIndices: 1, + numberOfIndicesChecked: 1, + numberOfSameFamily: 0, + sameFamilyFields: [], + sizeInBytes: undefined, + timeConsumedMs: 1500, + unallowedMappingFields: ['host.name', 'source.ip'], + unallowedValueFields: ['event.category'], + }); + expect(mockTelemetryEvents.reportDataQualityCheckAllCompleted).toHaveBeenCalledWith({ + batchId: 'test-batch', + ecsVersion: '8.11.0', + isCheckAll: true, + numberOfDocuments: 1000000, + numberOfIncompatibleFields: 3, + numberOfIndices: 1, + numberOfIndicesChecked: 1, + numberOfSameFamily: 0, + sizeInBytes: undefined, + timeConsumedMs: 1000, + }); + + expect(httpFetch).toHaveBeenCalledWith('/internal/ecs_data_quality_dashboard/results', { + method: 'POST', + version: '1', + signal: expect.any(AbortSignal), + body: expect.any(String), + }); + + const body = JSON.parse(httpFetch.mock.calls[0][1].body); + + expect(body).toEqual({ + batchId: 'test-batch', + indexName: '.ds-packetbeat-1', + indexPattern: 'packetbeat-*', + isCheckAll: true, + checkedAt: new Date('2021-10-07T00:00:02Z').getTime(), + docsCount: 1000000, + totalFieldCount: 9, + ecsFieldCount: 2, + customFieldCount: 4, + incompatibleFieldCount: 3, + incompatibleFieldMappingItems: [ + { + fieldName: 'host.name', + expectedValue: 'keyword', + actualValue: 'text', + description: + 'Name of the host.\nIt can contain what `hostname` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.', + }, + { + fieldName: 'source.ip', + expectedValue: 'ip', + actualValue: 'text', + description: 'IP address of the source (IPv4 or IPv6).', + }, + ], + incompatibleFieldValueItems: [ + { + fieldName: 'event.category', + expectedValues: [ + 'authentication', + 'configuration', + 'database', + 'driver', + 'email', + 'file', + 'host', + 'iam', + 'intrusion_detection', + 'malware', + 'network', + 'package', + 'process', + 'registry', + 'session', + 'threat', + 'vulnerability', + 'web', + ], + actualValues: [ + { name: 'an_invalid_category', count: 2 }, + { name: 'theory', count: 1 }, + ], + description: + 'This is one of four ECS Categorization Fields, and indicates the second level in the ECS category hierarchy.\n`event.category` represents the "big buckets" of ECS categories. For example, filtering on `event.category:process` yields all events relating to process activity. This field is closely related to `event.type`, which is used as a subcategory.\nThis field is an array. This will allow proper categorization of some events that fall in multiple categories.', + }, + ], + sameFamilyFieldCount: 0, + sameFamilyFields: [], + sameFamilyFieldItems: [], + unallowedMappingFields: ['host.name', 'source.ip'], + unallowedValueFields: ['event.category'], + ilmPhase: undefined, + sizeInBytes: 0, + markdownComments: [ + '### .ds-packetbeat-1\n', + '| Result | Index | Docs | Incompatible fields |\n|--------|-------|------|---------------------|\n| ❌ | .ds-packetbeat-1 | 1,000,000 (100.0%) | 3 |\n\n', + '### **Incompatible fields** `3` **Same family** `0` **Custom fields** `4` **ECS compliant fields** `2` **All fields** `9`\n', + "#### 3 incompatible fields\n\nFields are incompatible with ECS when index mappings, or the values of the fields in the index, don't conform to the Elastic Common Schema (ECS), version 8.11.0.\n\n❌ Detection engine rules referencing these fields may not match them correctly\n❌ Pages may not display some events or fields due to unexpected field mappings or values\n❌ Mappings or field values that don't comply with ECS are not supported\n", + '\n#### Incompatible field mappings - .ds-packetbeat-1\n\n\n| Field | ECS mapping type (expected) | Index mapping type (actual) | \n|-------|-----------------------------|-----------------------------|\n| host.name | `keyword` | `text` |\n| source.ip | `ip` | `text` |\n\n#### Incompatible field values - .ds-packetbeat-1\n\n\n| Field | ECS values (expected) | Document values (actual) | \n|-------|-----------------------|--------------------------|\n| event.category | `authentication`, `configuration`, `database`, `driver`, `email`, `file`, `host`, `iam`, `intrusion_detection`, `malware`, `network`, `package`, `process`, `registry`, `session`, `threat`, `vulnerability`, `web` | `an_invalid_category` (2), `theory` (1) |\n\n', + ], + ecsVersion: '8.11.0', + indexId: 'uuid-1', + error: null, + }); + }); + }); + }); + + describe('when check fails with error message and no partitionedFieldMetadata', () => { + it('should update patternRollup with error message, reset state without persisting in storage', () => { + const { result } = renderHook(() => + useResultsRollup({ + httpFetch, + toasts, + patterns, + isILMAvailable, + telemetryEvents: mockTelemetryEvents, + }) + ); + + const patternRollup = getPatternRollupStub('packetbeat-*', 1); + + act(() => { + result.current.updatePatternRollup(patternRollup); + }); + + const mockOnCheckCompletedOpts = { + batchId: 'test-batch', + checkAllStartTime: Date.now(), + error: 'Something went wrong', + formatBytes, + formatNumber, + indexName: '.ds-packetbeat-1', + partitionedFieldMetadata: null, + pattern: 'packetbeat-*', + requestTime: 1500, + isLastCheck: true, + isCheckAll: true, + }; + + act(() => { + result.current.onCheckCompleted(mockOnCheckCompletedOpts); + }); + + expect(result.current.patternRollups['packetbeat-*'].results?.['.ds-packetbeat-1']).toEqual( + { + checkedAt: undefined, + docsCount: 1000000, + error: 'Something went wrong', + ilmPhase: 'hot', + incompatible: undefined, + indexName: '.ds-packetbeat-1', + markdownComments: expect.any(Array), + pattern: 'packetbeat-*', + sameFamily: undefined, + } + ); + + expect(mockTelemetryEvents.reportDataQualityIndexChecked).not.toHaveBeenCalled(); + + expect(httpFetch).not.toHaveBeenCalledWith( + '/internal/ecs_data_quality_dashboard/results', + expect.any(Object) + ); + }); + }); + + describe('edge cases', () => { + describe('given no error nor partitionedFieldMetadata', () => { + it('should reset result state accordingly and not invoke telemetry report nor persist in storage', () => { + const { result } = renderHook(() => + useResultsRollup({ + httpFetch, + toasts, + patterns, + isILMAvailable, + telemetryEvents: mockTelemetryEvents, + }) + ); + + const patternRollup = getPatternRollupStub('packetbeat-*', 1); + + act(() => { + result.current.updatePatternRollup(patternRollup); + }); + + const mockOnCheckCompletedOpts = { + batchId: 'test-batch', + checkAllStartTime: Date.now(), + error: null, + formatBytes, + formatNumber, + indexName: '.ds-packetbeat-1', + partitionedFieldMetadata: null, + pattern: 'packetbeat-*', + requestTime: 1500, + isLastCheck: true, + isCheckAll: true, + }; + + act(() => { + result.current.onCheckCompleted(mockOnCheckCompletedOpts); + }); + + expect( + result.current.patternRollups['packetbeat-*'].results?.['.ds-packetbeat-1'] + ).toEqual({ + checkedAt: undefined, + docsCount: 1000000, + error: null, + ilmPhase: 'hot', + incompatible: undefined, + indexName: '.ds-packetbeat-1', + markdownComments: expect.any(Array), + pattern: 'packetbeat-*', + sameFamily: undefined, + }); + + expect(mockTelemetryEvents.reportDataQualityIndexChecked).not.toHaveBeenCalled(); + + expect(httpFetch).not.toHaveBeenCalledWith( + '/internal/ecs_data_quality_dashboard/results', + expect.any(Object) + ); + }); + }); + }); + }); + + describe('calculating totals', () => { + describe('when patternRollups change', () => { + it('should update totals', () => { + const { result } = renderHook(() => + useResultsRollup({ + httpFetch, + toasts, + patterns: ['packetbeat-*', 'auditbeat-*'], + isILMAvailable, + telemetryEvents: mockTelemetryEvents, + }) + ); + + const patternRollup1 = getPatternRollupStub('packetbeat-*', 1); + const patternRollup2 = getPatternRollupStub('auditbeat-*', 1); + + expect(result.current.totalIndices).toBe(0); + expect(result.current.totalDocsCount).toBe(0); + expect(result.current.totalSizeInBytes).toBe(0); + + act(() => { + result.current.updatePatternRollup(patternRollup1); + }); + + expect(result.current.totalIndices).toEqual(1); + expect(result.current.totalDocsCount).toEqual(1000000); + expect(result.current.totalSizeInBytes).toEqual(500000000); + + act(() => { + result.current.updatePatternRollup(patternRollup2); + }); + + expect(result.current.totalIndices).toEqual(2); + expect(result.current.totalDocsCount).toEqual(2000000); + expect(result.current.totalSizeInBytes).toEqual(1000000000); + }); + }); + }); +}); diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/hooks/use_results_rollup/index.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/hooks/use_results_rollup/index.tsx index 28b36765a245b..d95f1d1b7f20f 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/hooks/use_results_rollup/index.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/hooks/use_results_rollup/index.tsx @@ -21,83 +21,29 @@ import { getTotalPatternSameFamily, getIndexId, } from './utils/stats'; -import { - getStorageResults, - postStorageResult, - formatStorageResult, - formatResultFromStorage, -} from './utils/storage'; +import { postStorageResult, formatStorageResult } from './utils/storage'; import { getPatternRollupsWithLatestCheckResult } from './utils/get_pattern_rollups_with_latest_check_result'; -import type { - DataQualityCheckResult, - OnCheckCompleted, - PatternRollup, - TelemetryEvents, -} from '../../types'; +import type { OnCheckCompleted, PatternRollup, TelemetryEvents } from '../../types'; import { getEscapedIncompatibleMappingsFields, getEscapedIncompatibleValuesFields, getEscapedSameFamilyFields, } from './utils/metadata'; import { UseResultsRollupReturnValue } from './types'; -import { useIsMountedRef } from '../use_is_mounted_ref'; import { getDocsCount, getIndexIncompatible, getSizeInBytes } from '../../utils/stats'; import { getIlmPhase } from '../../utils/get_ilm_phase'; +import { useStoredPatternResults } from './hooks/use_stored_pattern_results'; interface Props { - ilmPhases: string[]; patterns: string[]; toasts: IToasts; httpFetch: HttpHandler; telemetryEvents: TelemetryEvents; isILMAvailable: boolean; } -const useStoredPatternResults = (patterns: string[], toasts: IToasts, httpFetch: HttpHandler) => { - const { isMountedRef } = useIsMountedRef(); - const [storedPatternResults, setStoredPatternResults] = useState< - Array<{ pattern: string; results: Record }> - >([]); - - useEffect(() => { - if (isEmpty(patterns)) { - return; - } - - let ignore = false; - const abortController = new AbortController(); - const fetchStoredPatternResults = async () => { - const requests = patterns.map((pattern) => - getStorageResults({ pattern, httpFetch, abortController, toasts }).then((results = []) => ({ - pattern, - results: Object.fromEntries( - results.map((storageResult) => [ - storageResult.indexName, - formatResultFromStorage({ storageResult, pattern }), - ]) - ), - })) - ); - const patternResults = await Promise.all(requests); - if (patternResults?.length && !ignore) { - if (isMountedRef.current) { - setStoredPatternResults(patternResults); - } - } - }; - - fetchStoredPatternResults(); - return () => { - ignore = true; - }; - }, [httpFetch, isMountedRef, patterns, toasts]); - - return storedPatternResults; -}; - export const useResultsRollup = ({ httpFetch, toasts, - ilmPhases, patterns, isILMAvailable, telemetryEvents, @@ -247,12 +193,6 @@ export const useResultsRollup = ({ [httpFetch, isILMAvailable, telemetryEvents, toasts] ); - useEffect(() => { - // reset all state - setPatternRollups({}); - setPatternIndexNames({}); - }, [ilmPhases, patterns]); - const useResultsRollupReturnValue = useMemo( () => ({ onCheckCompleted, diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/index.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/index.tsx index 7d1a106d83570..b6d2736d7e175 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/index.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/index.tsx @@ -104,7 +104,6 @@ const DataQualityPanelComponent: React.FC = ({ ); const resultsRollupHookReturnValue = useResultsRollup({ - ilmPhases, patterns, httpFetch, toasts, diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/mock/test_providers/utils/format.ts b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/mock/test_providers/utils/format.ts new file mode 100644 index 0000000000000..844b573b61cad --- /dev/null +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/mock/test_providers/utils/format.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 numeral from '@elastic/numeral'; + +import { EMPTY_STAT } from '../../../constants'; + +const defaultBytesFormat = '0,0.[0]b'; +export const formatBytes = (value: number | undefined) => + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + +const defaultNumberFormat = '0,0.[000]'; +export const formatNumber = (value: number | undefined) => + value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT; diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/mock/test_providers/utils/get_merged_data_quality_context_props.ts b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/mock/test_providers/utils/get_merged_data_quality_context_props.ts index 264198e510b5e..a8df6818605a1 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/mock/test_providers/utils/get_merged_data_quality_context_props.ts +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/mock/test_providers/utils/get_merged_data_quality_context_props.ts @@ -5,10 +5,9 @@ * 2.0. */ -import numeral from '@elastic/numeral'; - import { DataQualityProviderProps } from '../../../data_quality_context'; -import { EMPTY_STAT } from '../../../constants'; + +import { formatBytes as formatBytesMock, formatNumber as formatNumberMock } from './format'; export const getMergedDataQualityContextProps = ( dataQualityContextProps?: Partial @@ -36,10 +35,8 @@ export const getMergedDataQualityContextProps = ( addSuccessToast: jest.fn(), canUserCreateAndReadCases: jest.fn(() => true), endDate: null, - formatBytes: (value: number | undefined) => - value != null ? numeral(value).format('0,0.[0]b') : EMPTY_STAT, - formatNumber: (value: number | undefined) => - value != null ? numeral(value).format('0,0.[000]') : EMPTY_STAT, + formatBytes: formatBytesMock, + formatNumber: formatNumberMock, isAssistantEnabled: true, lastChecked: '2023-03-28T22:27:28.159Z', openCreateCaseFlyout: jest.fn(), diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/stub/get_pattern_rollup_stub/index.ts b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/stub/get_pattern_rollup_stub/index.ts new file mode 100644 index 0000000000000..38aa129a6ec9a --- /dev/null +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality_panel/stub/get_pattern_rollup_stub/index.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 { PatternRollup } from '../../types'; + +const phases = ['hot', 'warm', 'cold', 'frozen'] as const; + +/** + * + * This function derives ilmExplain, results, stats and ilmExplainPhaseCounts + * from the provided pattern and indicesCount for the purpose of simplifying + * stubbing of resultsRollup in tests. + * + * @param pattern - The index pattern to simulate. Defaults to `'packetbeat-*'`. + * @param indicesCount - The number of indices to generate. Defaults to `2`. + * @param isILMAvailable - Whether ILM is available. Defaults to `true`. + * @returns An object containing stubbed pattern rollup data + */ +export const getPatternRollupStub = ( + pattern = 'packetbeat-*', + indicesCount = 2, + isILMAvailable = true +): PatternRollup => { + // Derive ilmExplain from isILMAvailable, pattern and indicesCount + const ilmExplain = isILMAvailable + ? Object.fromEntries( + Array.from({ length: indicesCount }).map((_, i) => { + const indexName = pattern.replace('*', `${i + 1}`); + const dsIndexName = `.ds-${indexName}`; + // Cycle through phases + const phase = phases[i % phases.length]; + return [ + dsIndexName, + { + index: dsIndexName, + managed: true, + policy: pattern, + phase, + }, + ]; + }) + ) + : null; + + // Derive ilmExplainPhaseCounts from ilmExplain + const ilmExplainPhaseCounts = ilmExplain + ? phases.reduce( + (counts, phase) => ({ + ...counts, + [phase]: Object.values(ilmExplain).filter((explain) => explain.phase === phase).length, + }), + { hot: 0, warm: 0, cold: 0, frozen: 0, unmanaged: 0 } + ) + : undefined; + + // Derive results from pattern and indicesCount + const results = Object.fromEntries( + Array.from({ length: indicesCount }, (_, i) => { + const indexName = pattern.replace('*', `${i + 1}`); + const dsIndexName = `.ds-${indexName}`; + return [ + dsIndexName, + { + docsCount: 1000000 + i * 100000, // Example doc count + error: null, + ilmPhase: ilmExplain?.[dsIndexName].phase, + incompatible: i, + indexName: dsIndexName, + markdownComments: ['foo', 'bar', 'baz'], + pattern, + sameFamily: i, + checkedAt: Date.now(), + }, + ]; + }) + ); + + // Derive stats from isILMAvailable, pattern and indicesCount + const stats = Object.fromEntries( + Array.from({ length: indicesCount }, (_, i) => { + const indexName = pattern.replace('*', `${i + 1}`); + const dsIndexName = `.ds-${indexName}`; + return [ + dsIndexName, + { + uuid: `uuid-${i + 1}`, + size_in_bytes: isILMAvailable ? 500000000 + i * 10000000 : null, + name: dsIndexName, + num_docs: results[dsIndexName].docsCount, + }, + ]; + }) + ); + + // Derive total docsCount and sizeInBytes from stats + const totalDocsCount = Object.values(stats).reduce((sum, stat) => sum + stat.num_docs, 0); + const totalSizeInBytes = isILMAvailable + ? Object.values(stats).reduce((sum, stat) => sum + (stat.size_in_bytes ?? 0), 0) + : undefined; + + return { + docsCount: totalDocsCount, + error: null, + pattern, + ilmExplain, + ilmExplainPhaseCounts, + indices: indicesCount, + results, + sizeInBytes: totalSizeInBytes, + stats, + }; +}; diff --git a/x-pack/plugins/actions/kibana.jsonc b/x-pack/plugins/actions/kibana.jsonc index 3cb9e8bfd79c5..882c832245951 100644 --- a/x-pack/plugins/actions/kibana.jsonc +++ b/x-pack/plugins/actions/kibana.jsonc @@ -26,7 +26,8 @@ "spaces", "security", "monitoringCollection", - "serverless" + "serverless", + "cloud" ], "extraPublicDirs": [ "common" diff --git a/x-pack/plugins/actions/server/actions_client/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client/actions_client.test.ts index 7f15dd6287d6b..99bcbf15ecfb9 100644 --- a/x-pack/plugins/actions/server/actions_client/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client/actions_client.test.ts @@ -39,13 +39,7 @@ import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/us import { actionExecutorMock } from '../lib/action_executor.mock'; import { v4 as uuidv4 } from 'uuid'; import { ActionsAuthorization } from '../authorization/actions_authorization'; -import { - getAuthorizationModeBySource, - AuthorizationMode, - bulkGetAuthorizationModeBySource, -} from '../authorization/get_authorization_mode_by_source'; import { actionsAuthorizationMock } from '../authorization/actions_authorization.mock'; -import { trackLegacyRBACExemption } from '../lib/track_legacy_rbac_exemption'; import { ConnectorTokenClient } from '../lib/connector_token_client'; import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; import { SavedObject } from '@kbn/core/server'; @@ -57,6 +51,7 @@ import { OAuthParams } from '../routes/get_oauth_access_token'; import { eventLogClientMock } from '@kbn/event-log-plugin/server/event_log_client.mock'; import { GetGlobalExecutionKPIParams, GetGlobalExecutionLogParams } from '../../common'; import { estypes } from '@elastic/elasticsearch'; +import { DEFAULT_USAGE_API_URL } from '../config'; jest.mock('@kbn/core-saved-objects-utils-server', () => { const actual = jest.requireActual('@kbn/core-saved-objects-utils-server'); @@ -68,25 +63,6 @@ jest.mock('@kbn/core-saved-objects-utils-server', () => { }; }); -jest.mock('../lib/track_legacy_rbac_exemption', () => ({ - trackLegacyRBACExemption: jest.fn(), -})); - -jest.mock('../authorization/get_authorization_mode_by_source', () => { - return { - getAuthorizationModeBySource: jest.fn(() => { - return 1; - }), - bulkGetAuthorizationModeBySource: jest.fn(() => { - return 1; - }), - AuthorizationMode: { - Legacy: 0, - RBAC: 1, - }, - }; -}); - jest.mock('../lib/get_oauth_jwt_access_token', () => ({ getOAuthJwtAccessToken: jest.fn(), })); @@ -599,8 +575,6 @@ describe('create()', () => { allowedHosts: ['*'], preconfiguredAlertHistoryEsIndex: false, preconfigured: {}, - proxyRejectUnauthorizedCertificates: true, // legacy - rejectUnauthorized: true, // legacy proxyBypassHosts: undefined, proxyOnlyHosts: undefined, maxResponseContentLength: new ByteSizeValue(1000000), @@ -613,6 +587,9 @@ describe('create()', () => { microsoftGraphApiUrl: DEFAULT_MICROSOFT_GRAPH_API_URL, microsoftGraphApiScope: DEFAULT_MICROSOFT_GRAPH_API_SCOPE, microsoftExchangeUrl: DEFAULT_MICROSOFT_EXCHANGE_URL, + usage: { + url: DEFAULT_USAGE_API_URL, + }, }); const localActionTypeRegistryParams = { @@ -2745,9 +2722,6 @@ describe('update()', () => { describe('execute()', () => { describe('authorization', () => { test('ensures user is authorised to excecute actions', async () => { - (getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => { - return AuthorizationMode.RBAC; - }); unsecuredSavedObjectsClient.get.mockResolvedValueOnce(actionTypeIdFromSavedObjectMock()); await actionsClient.execute({ actionId: 'action-id', @@ -2764,9 +2738,6 @@ describe('execute()', () => { }); test('throws when user is not authorised to create the type of action', async () => { - (getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => { - return AuthorizationMode.RBAC; - }); authorization.ensureAuthorized.mockRejectedValue( new Error(`Unauthorized to execute all actions`) ); @@ -2790,28 +2761,7 @@ describe('execute()', () => { }); }); - test('tracks legacy RBAC', async () => { - (getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => { - return AuthorizationMode.Legacy; - }); - - await actionsClient.execute({ - actionId: 'action-id', - params: { - name: 'my name', - }, - source: asHttpRequestExecutionSource(request), - }); - - expect(trackLegacyRBACExemption as jest.Mock).toBeCalledWith('execute', mockUsageCounter); - expect(authorization.ensureAuthorized).not.toHaveBeenCalled(); - }); - test('ensures that system actions privileges are being authorized correctly', async () => { - (getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => { - return AuthorizationMode.RBAC; - }); - actionsClient = new ActionsClient({ inMemoryConnectors: [ { @@ -2872,10 +2822,6 @@ describe('execute()', () => { }); test('does not authorize kibana privileges for non system actions', async () => { - (getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => { - return AuthorizationMode.RBAC; - }); - actionsClient = new ActionsClient({ inMemoryConnectors: [ { @@ -2939,10 +2885,6 @@ describe('execute()', () => { }); test('pass the params to the actionTypeRegistry when authorizing system actions', async () => { - (getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => { - return AuthorizationMode.RBAC; - }); - const getKibanaPrivileges = jest.fn().mockReturnValue(['test/create']); actionsClient = new ActionsClient({ @@ -3106,9 +3048,6 @@ describe('execute()', () => { describe('bulkEnqueueExecution()', () => { describe('authorization', () => { test('ensures user is authorised to execute actions', async () => { - (bulkGetAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => { - return { [AuthorizationMode.RBAC]: 1, [AuthorizationMode.Legacy]: 0 }; - }); await actionsClient.bulkEnqueueExecution([ { id: uuidv4(), @@ -3136,9 +3075,6 @@ describe('bulkEnqueueExecution()', () => { }); test('throws when user is not authorised to create the type of action', async () => { - (bulkGetAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => { - return { [AuthorizationMode.RBAC]: 1, [AuthorizationMode.Legacy]: 0 }; - }); authorization.ensureAuthorized.mockRejectedValue( new Error(`Unauthorized to execute all actions`) ); @@ -3170,45 +3106,9 @@ describe('bulkEnqueueExecution()', () => { operation: 'execute', }); }); - - test('tracks legacy RBAC', async () => { - (bulkGetAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => { - return { [AuthorizationMode.RBAC]: 0, [AuthorizationMode.Legacy]: 2 }; - }); - - await actionsClient.bulkEnqueueExecution([ - { - id: uuidv4(), - params: {}, - spaceId: 'default', - executionId: '123abc', - apiKey: null, - source: asHttpRequestExecutionSource(request), - actionTypeId: 'my-action-type', - }, - { - id: uuidv4(), - params: {}, - spaceId: 'default', - executionId: '456def', - apiKey: null, - source: asHttpRequestExecutionSource(request), - actionTypeId: 'my-action-type', - }, - ]); - - expect(trackLegacyRBACExemption as jest.Mock).toBeCalledWith( - 'bulkEnqueueExecution', - mockUsageCounter, - 2 - ); - }); }); test('calls the bulkExecutionEnqueuer with the appropriate parameters', async () => { - (bulkGetAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => { - return { [AuthorizationMode.RBAC]: 0, [AuthorizationMode.Legacy]: 0 }; - }); const opts = [ { id: uuidv4(), @@ -3504,17 +3404,11 @@ describe('getGlobalExecutionLogWithAuth()', () => { test('ensures user is authorised to access logs', async () => { eventLogClient.aggregateEventsWithAuthFilter.mockResolvedValue(results); - (getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => { - return AuthorizationMode.RBAC; - }); await actionsClient.getGlobalExecutionLogWithAuth(opts); expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); }); test('throws when user is not authorised to access logs', async () => { - (getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => { - return AuthorizationMode.RBAC; - }); authorization.ensureAuthorized.mockRejectedValue(new Error(`Unauthorized to access logs`)); await expect(actionsClient.getGlobalExecutionLogWithAuth(opts)).rejects.toMatchInlineSnapshot( @@ -3563,17 +3457,11 @@ describe('getGlobalExecutionKpiWithAuth()', () => { test('ensures user is authorised to access kpi', async () => { eventLogClient.aggregateEventsWithAuthFilter.mockResolvedValue(results); - (getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => { - return AuthorizationMode.RBAC; - }); await actionsClient.getGlobalExecutionKpiWithAuth(opts); expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); }); test('throws when user is not authorised to access kpi', async () => { - (getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => { - return AuthorizationMode.RBAC; - }); authorization.ensureAuthorized.mockRejectedValue(new Error(`Unauthorized to access kpi`)); await expect(actionsClient.getGlobalExecutionKpiWithAuth(opts)).rejects.toMatchInlineSnapshot( diff --git a/x-pack/plugins/actions/server/actions_client/actions_client.ts b/x-pack/plugins/actions/server/actions_client/actions_client.ts index de029ed2acf54..5c563fbd6aa82 100644 --- a/x-pack/plugins/actions/server/actions_client/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client/actions_client.ts @@ -10,7 +10,7 @@ import url from 'url'; import { UsageCounter } from '@kbn/usage-collection-plugin/server'; import { i18n } from '@kbn/i18n'; -import { compact, uniq } from 'lodash'; +import { uniq } from 'lodash'; import { IScopedClusterClient, SavedObjectsClientContract, @@ -35,7 +35,7 @@ import { IExecutionLogResult, } from '../../common'; import { ActionTypeRegistry } from '../action_type_registry'; -import { ActionExecutorContract, ActionExecutionSource, parseDate } from '../lib'; +import { ActionExecutorContract, parseDate } from '../lib'; import { ActionResult, RawAction, @@ -52,13 +52,7 @@ import { ExecutionResponse, } from '../create_execute_function'; import { ActionsAuthorization } from '../authorization/actions_authorization'; -import { - getAuthorizationModeBySource, - bulkGetAuthorizationModeBySource, - AuthorizationMode, -} from '../authorization/get_authorization_mode_by_source'; import { connectorAuditEvent, ConnectorAuditAction } from '../lib/audit_events'; -import { trackLegacyRBACExemption } from '../lib/track_legacy_rbac_exemption'; import { ActionsConfigurationUtilities } from '../actions_config'; import { OAuthClientCredentialsParams, @@ -496,51 +490,26 @@ export class ActionsClient { public async bulkEnqueueExecution( options: EnqueueExecutionOptions[] ): Promise { - const sources: Array> = compact( - (options ?? []).map((option) => option.source) - ); - - const authModes = await bulkGetAuthorizationModeBySource( - this.context.logger, - this.context.unsecuredSavedObjectsClient, - sources + /** + * For scheduled executions the additional authorization check + * for system actions (kibana privileges) will be performed + * inside the ActionExecutor at execution time + */ + await this.context.authorization.ensureAuthorized({ operation: 'execute' }); + await Promise.all( + uniq(options.map((o) => o.actionTypeId)).map((actionTypeId) => + this.context.authorization.ensureAuthorized({ operation: 'execute', actionTypeId }) + ) ); - if (authModes[AuthorizationMode.RBAC] > 0) { - /** - * For scheduled executions the additional authorization check - * for system actions (kibana privileges) will be performed - * inside the ActionExecutor at execution time - */ - await this.context.authorization.ensureAuthorized({ operation: 'execute' }); - await Promise.all( - uniq(options.map((o) => o.actionTypeId)).map((actionTypeId) => - this.context.authorization.ensureAuthorized({ operation: 'execute', actionTypeId }) - ) - ); - } - if (authModes[AuthorizationMode.Legacy] > 0) { - trackLegacyRBACExemption( - 'bulkEnqueueExecution', - this.context.usageCounter, - authModes[AuthorizationMode.Legacy] - ); - } return this.context.bulkExecutionEnqueuer(this.context.unsecuredSavedObjectsClient, options); } public async ephemeralEnqueuedExecution(options: EnqueueExecutionOptions): Promise { - const { source } = options; - if ( - (await getAuthorizationModeBySource(this.context.unsecuredSavedObjectsClient, source)) === - AuthorizationMode.RBAC - ) { - await this.context.authorization.ensureAuthorized({ - operation: 'execute', - actionTypeId: options.actionTypeId, - }); - } else { - trackLegacyRBACExemption('ephemeralEnqueuedExecution', this.context.usageCounter); - } + await this.context.authorization.ensureAuthorized({ + operation: 'execute', + actionTypeId: options.actionTypeId, + }); + return this.context.ephemeralExecutionEnqueuer( this.context.unsecuredSavedObjectsClient, options diff --git a/x-pack/plugins/actions/server/actions_config.test.ts b/x-pack/plugins/actions/server/actions_config.test.ts index a6966e0e85c40..ce16aca508af6 100644 --- a/x-pack/plugins/actions/server/actions_config.test.ts +++ b/x-pack/plugins/actions/server/actions_config.test.ts @@ -6,7 +6,7 @@ */ import { ByteSizeValue } from '@kbn/config-schema'; -import { ActionsConfig } from './config'; +import { ActionsConfig, DEFAULT_USAGE_API_URL } from './config'; import { DEFAULT_MICROSOFT_EXCHANGE_URL, DEFAULT_MICROSOFT_GRAPH_API_SCOPE, @@ -30,8 +30,6 @@ const defaultActionsConfig: ActionsConfig = { enabledActionTypes: [], preconfiguredAlertHistoryEsIndex: false, preconfigured: {}, - proxyRejectUnauthorizedCertificates: true, // legacy - rejectUnauthorized: true, // legacy maxResponseContentLength: new ByteSizeValue(1000000), responseTimeout: moment.duration(60000), ssl: { @@ -42,6 +40,9 @@ const defaultActionsConfig: ActionsConfig = { microsoftGraphApiUrl: DEFAULT_MICROSOFT_GRAPH_API_URL, microsoftGraphApiScope: DEFAULT_MICROSOFT_GRAPH_API_SCOPE, microsoftExchangeUrl: DEFAULT_MICROSOFT_EXCHANGE_URL, + usage: { + url: DEFAULT_USAGE_API_URL, + }, }; describe('ensureUriAllowed', () => { @@ -315,25 +316,6 @@ describe('getProxySettings', () => { expect(proxySettings?.proxyUrl).toBe(config.proxyUrl); }); - test('returns proper verificationMode values, beased on the legacy config option proxyRejectUnauthorizedCertificates', () => { - const configTrue: ActionsConfig = { - ...defaultActionsConfig, - proxyUrl: 'https://proxy.elastic.co', - proxyRejectUnauthorizedCertificates: true, - }; - let proxySettings = getActionsConfigurationUtilities(configTrue).getProxySettings(); - expect(proxySettings?.proxySSLSettings.verificationMode).toBe('full'); - - const configFalse: ActionsConfig = { - ...defaultActionsConfig, - proxyUrl: 'https://proxy.elastic.co', - proxyRejectUnauthorizedCertificates: false, - ssl: {}, - }; - proxySettings = getActionsConfigurationUtilities(configFalse).getProxySettings(); - expect(proxySettings?.proxySSLSettings.verificationMode).toBe('none'); - }); - test('returns proper verificationMode value, based on the SSL proxy configuration', () => { const configTrue: ActionsConfig = { ...defaultActionsConfig, diff --git a/x-pack/plugins/actions/server/actions_config.ts b/x-pack/plugins/actions/server/actions_config.ts index e77c0528d16a1..30c96b4b6a998 100644 --- a/x-pack/plugins/actions/server/actions_config.ts +++ b/x-pack/plugins/actions/server/actions_config.ts @@ -122,10 +122,7 @@ function getProxySettingsFromConfig(config: ActionsConfig): undefined | ProxySet proxyBypassHosts: arrayAsSet(config.proxyBypassHosts), proxyOnlyHosts: arrayAsSet(config.proxyOnlyHosts), proxyHeaders: config.proxyHeaders, - proxySSLSettings: getSSLSettingsFromConfig( - config.ssl?.proxyVerificationMode, - config.proxyRejectUnauthorizedCertificates - ), + proxySSLSettings: getSSLSettingsFromConfig(config.ssl?.proxyVerificationMode), }; } @@ -200,8 +197,7 @@ export function getActionsConfigurationUtilities( isActionTypeEnabled, getProxySettings: () => getProxySettingsFromConfig(config), getResponseSettings: () => getResponseSettingsFromConfig(config), - getSSLSettings: () => - getSSLSettingsFromConfig(config.ssl?.verificationMode, config.rejectUnauthorized), + getSSLSettings: () => getSSLSettingsFromConfig(config.ssl?.verificationMode), ensureUriAllowed(uri: string) { if (!isUriAllowed(uri)) { throw new Error(allowListErrorMessage(AllowListingField.URL, uri)); diff --git a/x-pack/plugins/actions/server/application/connector/methods/execute/execute.ts b/x-pack/plugins/actions/server/application/connector/methods/execute/execute.ts index f9922e0b61a8d..fc96f3a38caf5 100644 --- a/x-pack/plugins/actions/server/application/connector/methods/execute/execute.ts +++ b/x-pack/plugins/actions/server/application/connector/methods/execute/execute.ts @@ -10,11 +10,6 @@ import { RawAction, ActionTypeExecutorResult } from '../../../../types'; import { getSystemActionKibanaPrivileges } from '../../../../lib/get_system_action_kibana_privileges'; import { isPreconfigured } from '../../../../lib/is_preconfigured'; import { isSystemAction } from '../../../../lib/is_system_action'; -import { - getAuthorizationModeBySource, - AuthorizationMode, -} from '../../../../authorization/get_authorization_mode_by_source'; -import { trackLegacyRBACExemption } from '../../../../lib/track_legacy_rbac_exemption'; import { ConnectorExecuteParams } from './types'; import { ACTION_SAVED_OBJECT_TYPE } from '../../../../constants/saved_objects'; import { ActionsClientContext } from '../../../../actions_client'; @@ -25,43 +20,34 @@ export async function execute( ): Promise> { const log = context.logger; const { actionId, params, source, relatedSavedObjects } = connectorExecuteParams; - - if ( - (await getAuthorizationModeBySource(context.unsecuredSavedObjectsClient, source)) === - AuthorizationMode.RBAC - ) { - const additionalPrivileges = getSystemActionKibanaPrivileges(context, actionId, params); - let actionTypeId: string | undefined; - - try { - if (isPreconfigured(context, actionId) || isSystemAction(context, actionId)) { - const connector = context.inMemoryConnectors.find( - (inMemoryConnector) => inMemoryConnector.id === actionId - ); - - actionTypeId = connector?.actionTypeId; - } else { - // TODO: Optimize so we don't do another get on top of getAuthorizationModeBySource and within the actionExecutor.execute - const { attributes } = await context.unsecuredSavedObjectsClient.get( - ACTION_SAVED_OBJECT_TYPE, - actionId - ); - - actionTypeId = attributes.actionTypeId; - } - } catch (err) { - log.debug(`Failed to retrieve actionTypeId for action [${actionId}]`, err); + const additionalPrivileges = getSystemActionKibanaPrivileges(context, actionId, params); + let actionTypeId: string | undefined; + + try { + if (isPreconfigured(context, actionId) || isSystemAction(context, actionId)) { + const connector = context.inMemoryConnectors.find( + (inMemoryConnector) => inMemoryConnector.id === actionId + ); + + actionTypeId = connector?.actionTypeId; + } else { + const { attributes } = await context.unsecuredSavedObjectsClient.get( + ACTION_SAVED_OBJECT_TYPE, + actionId + ); + + actionTypeId = attributes.actionTypeId; } - - await context.authorization.ensureAuthorized({ - operation: 'execute', - additionalPrivileges, - actionTypeId, - }); - } else { - trackLegacyRBACExemption('execute', context.usageCounter); + } catch (err) { + log.debug(`Failed to retrieve actionTypeId for action [${actionId}]`, err); } + await context.authorization.ensureAuthorized({ + operation: 'execute', + additionalPrivileges, + actionTypeId, + }); + return context.actionExecutor.execute({ actionId, params, diff --git a/x-pack/plugins/actions/server/application/connector/methods/get_all/get_all.test.ts b/x-pack/plugins/actions/server/application/connector/methods/get_all/get_all.test.ts index ee11cf6a35d82..0f86a8e582e34 100644 --- a/x-pack/plugins/actions/server/application/connector/methods/get_all/get_all.test.ts +++ b/x-pack/plugins/actions/server/application/connector/methods/get_all/get_all.test.ts @@ -36,25 +36,6 @@ jest.mock('@kbn/core-saved-objects-utils-server', () => { }; }); -jest.mock('../../../../lib/track_legacy_rbac_exemption', () => ({ - trackLegacyRBACExemption: jest.fn(), -})); - -jest.mock('../../../../authorization/get_authorization_mode_by_source', () => { - return { - getAuthorizationModeBySource: jest.fn(() => { - return 1; - }), - bulkGetAuthorizationModeBySource: jest.fn(() => { - return 1; - }), - AuthorizationMode: { - Legacy: 0, - RBAC: 1, - }, - }; -}); - jest.mock('../../../../lib/get_oauth_jwt_access_token', () => ({ getOAuthJwtAccessToken: jest.fn(), })); diff --git a/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts b/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts index 41eab4fbc2e43..7755071e69c24 100644 --- a/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts +++ b/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts @@ -12,7 +12,6 @@ import { ACTION_SAVED_OBJECT_TYPE, ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, } from '../constants/saved_objects'; -import { AuthorizationMode } from './get_authorization_mode_by_source'; import { CONNECTORS_ADVANCED_EXECUTE_PRIVILEGE_API_TAG, CONNECTORS_BASIC_EXECUTE_PRIVILEGE_API_TAG, @@ -164,24 +163,6 @@ describe('ensureAuthorized', () => { ).rejects.toThrowErrorMatchingInlineSnapshot(`"Unauthorized to create a \\"myType\\" action"`); }); - test('exempts users from requiring privileges to execute actions when authorizationMode is Legacy', async () => { - const { authorization } = mockSecurity(); - const checkPrivileges: jest.MockedFunction< - ReturnType - > = jest.fn(); - authorization.checkPrivilegesDynamicallyWithRequest.mockReturnValue(checkPrivileges); - const actionsAuthorization = new ActionsAuthorization({ - request, - authorization, - authorizationMode: AuthorizationMode.Legacy, - }); - - await actionsAuthorization.ensureAuthorized({ operation: 'execute', actionTypeId: 'myType' }); - - expect(authorization.actions.savedObject.get).not.toHaveBeenCalled(); - expect(checkPrivileges).not.toHaveBeenCalled(); - }); - test('checks additional privileges correctly', async () => { const { authorization } = mockSecurity(); const checkPrivileges: jest.MockedFunction< diff --git a/x-pack/plugins/actions/server/authorization/actions_authorization.ts b/x-pack/plugins/actions/server/authorization/actions_authorization.ts index 5739af64050ee..e392f8bbcc14a 100644 --- a/x-pack/plugins/actions/server/authorization/actions_authorization.ts +++ b/x-pack/plugins/actions/server/authorization/actions_authorization.ts @@ -13,19 +13,10 @@ import { ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, } from '../constants/saved_objects'; import { isBidirectionalConnectorType } from '../lib/bidirectional_connectors'; -import { AuthorizationMode } from './get_authorization_mode_by_source'; export interface ConstructorOptions { request: KibanaRequest; authorization?: SecurityPluginSetup['authz']; - // In order to support legacy Alerts which predate the introduction of the - // Actions feature in Kibana we need a way of "dialing down" the level of - // authorization for certain opearations. - // Specifically, we want to allow these old alerts and their scheduled - // actions to continue to execute - which requires that we exempt auth on - // `get` for Connectors and `execute` for Action execution when used by - // these legacy alerts - authorizationMode?: AuthorizationMode; } const operationAlias: Record string[]> = { @@ -38,21 +29,13 @@ const operationAlias: Record; - -describe(`#getAuthorizationModeBySource`, () => { - test('should return RBAC if no source is provided', async () => { - expect(await getAuthorizationModeBySource(unsecuredSavedObjectsClient)).toEqual( - AuthorizationMode.RBAC - ); - }); - - test('should return RBAC if source is not an alert', async () => { - expect( - await getAuthorizationModeBySource( - unsecuredSavedObjectsClient, - asSavedObjectExecutionSource({ - type: 'action', - id: uuidv4(), - }) - ) - ).toEqual(AuthorizationMode.RBAC); - }); - - test('should return RBAC if source alert is not marked as legacy', async () => { - const id = uuidv4(); - unsecuredSavedObjectsClient.get.mockResolvedValue(mockRuleSO({ id })); - expect( - await getAuthorizationModeBySource( - unsecuredSavedObjectsClient, - asSavedObjectExecutionSource({ - type: 'alert', - id, - }) - ) - ).toEqual(AuthorizationMode.RBAC); - }); - - test('should return Legacy if source alert is marked as legacy', async () => { - const id = uuidv4(); - unsecuredSavedObjectsClient.get.mockResolvedValue( - mockRuleSO({ id, attributes: { meta: { versionApiKeyLastmodified: 'pre-7.10.0' } } }) - ); - expect( - await getAuthorizationModeBySource( - unsecuredSavedObjectsClient, - asSavedObjectExecutionSource({ - type: 'alert', - id, - }) - ) - ).toEqual(AuthorizationMode.Legacy); - }); - - test('should return RBAC if source alert is marked as modern', async () => { - const id = uuidv4(); - unsecuredSavedObjectsClient.get.mockResolvedValue( - mockRuleSO({ id, attributes: { meta: { versionApiKeyLastmodified: '7.10.0' } } }) - ); - expect( - await getAuthorizationModeBySource( - unsecuredSavedObjectsClient, - asSavedObjectExecutionSource({ - type: 'alert', - id, - }) - ) - ).toEqual(AuthorizationMode.RBAC); - }); - - test('should return RBAC if source alert doesnt have a last modified version', async () => { - const id = uuidv4(); - unsecuredSavedObjectsClient.get.mockResolvedValue(mockRuleSO({ id, attributes: { meta: {} } })); - expect( - await getAuthorizationModeBySource( - unsecuredSavedObjectsClient, - asSavedObjectExecutionSource({ - type: 'alert', - id, - }) - ) - ).toEqual(AuthorizationMode.RBAC); - }); -}); - -describe(`#bulkGetAuthorizationModeBySource`, () => { - beforeEach(() => { - jest.resetAllMocks(); - }); - - test('should return RBAC if no sources are provided', async () => { - expect(await bulkGetAuthorizationModeBySource(logger, unsecuredSavedObjectsClient)).toEqual({ - [AuthorizationMode.RBAC]: 1, - [AuthorizationMode.Legacy]: 0, - }); - expect(unsecuredSavedObjectsClient.bulkGet).not.toHaveBeenCalled(); - }); - - test('should return RBAC if no alert sources are provided', async () => { - expect( - await bulkGetAuthorizationModeBySource(logger, unsecuredSavedObjectsClient, [ - asSavedObjectExecutionSource({ - type: 'action', - id: uuidv4(), - }), - asHttpRequestExecutionSource({} as KibanaRequest), - ]) - ).toEqual({ [AuthorizationMode.RBAC]: 1, [AuthorizationMode.Legacy]: 0 }); - - expect(unsecuredSavedObjectsClient.bulkGet).not.toHaveBeenCalled(); - }); - - test('should consolidate duplicate alert sources', async () => { - unsecuredSavedObjectsClient.bulkGet.mockResolvedValue({ - saved_objects: [mockRuleSO({ id: '1' }), mockRuleSO({ id: '2' })], - }); - expect( - await bulkGetAuthorizationModeBySource(logger, unsecuredSavedObjectsClient, [ - asSavedObjectExecutionSource({ - type: 'alert', - id: '1', - }), - asSavedObjectExecutionSource({ - type: 'alert', - id: '1', - }), - asSavedObjectExecutionSource({ - type: 'alert', - id: '2', - }), - ]) - ).toEqual({ [AuthorizationMode.RBAC]: 2, [AuthorizationMode.Legacy]: 0 }); - - expect(unsecuredSavedObjectsClient.bulkGet).toHaveBeenCalledWith([ - { - type: 'alert', - id: '1', - }, - { - type: 'alert', - id: '2', - }, - ]); - }); - - test('should return RBAC if source alert is not marked as legacy', async () => { - const id = uuidv4(); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValue({ saved_objects: [mockRuleSO({ id })] }); - expect( - await bulkGetAuthorizationModeBySource(logger, unsecuredSavedObjectsClient, [ - asSavedObjectExecutionSource({ - type: 'alert', - id, - }), - ]) - ).toEqual({ [AuthorizationMode.RBAC]: 1, [AuthorizationMode.Legacy]: 0 }); - }); - - test('should return Legacy if source alert is marked as legacy', async () => { - const id = uuidv4(); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValue({ - saved_objects: [ - mockRuleSO({ id, attributes: { meta: { versionApiKeyLastmodified: 'pre-7.10.0' } } }), - ], - }); - expect( - await bulkGetAuthorizationModeBySource(logger, unsecuredSavedObjectsClient, [ - asSavedObjectExecutionSource({ - type: 'alert', - id, - }), - ]) - ).toEqual({ [AuthorizationMode.RBAC]: 0, [AuthorizationMode.Legacy]: 1 }); - }); - - test('should return RBAC if source alert is marked as modern', async () => { - const id = uuidv4(); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValue({ - saved_objects: [ - mockRuleSO({ id, attributes: { meta: { versionApiKeyLastmodified: '7.10.0' } } }), - ], - }); - expect( - await bulkGetAuthorizationModeBySource(logger, unsecuredSavedObjectsClient, [ - asSavedObjectExecutionSource({ - type: 'alert', - id, - }), - ]) - ).toEqual({ [AuthorizationMode.RBAC]: 1, [AuthorizationMode.Legacy]: 0 }); - }); - - test('should return RBAC if source alert doesnt have a last modified version', async () => { - const id = uuidv4(); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValue({ - saved_objects: [mockRuleSO({ id, attributes: { meta: {} } })], - }); - expect( - await bulkGetAuthorizationModeBySource(logger, unsecuredSavedObjectsClient, [ - asSavedObjectExecutionSource({ - type: 'alert', - id, - }), - ]) - ).toEqual({ [AuthorizationMode.RBAC]: 1, [AuthorizationMode.Legacy]: 0 }); - }); - - test('should return RBAC and log warning if error getting source alert', async () => { - unsecuredSavedObjectsClient.bulkGet.mockResolvedValue({ - saved_objects: [ - mockRuleSO({ id: '1', attributes: { meta: { versionApiKeyLastmodified: 'pre-7.10.0' } } }), - // @ts-expect-error - { - id: '2', - type: 'alert', - error: { statusCode: 404, error: 'failed to get', message: 'fail' }, - }, - ], - }); - expect( - await bulkGetAuthorizationModeBySource(logger, unsecuredSavedObjectsClient, [ - asSavedObjectExecutionSource({ - type: 'alert', - id: '1', - }), - asSavedObjectExecutionSource({ - type: 'alert', - id: '2', - }), - ]) - ).toEqual({ [AuthorizationMode.RBAC]: 1, [AuthorizationMode.Legacy]: 1 }); - - expect(logger.warn).toHaveBeenCalledWith( - `Error retrieving saved object [alert/2] - fail - default to using RBAC authorization mode.` - ); - }); -}); - -const mockRuleSO = (overrides: Record = {}) => ({ - id: '1', - type: 'alert', - attributes: { - consumer: 'myApp', - schedule: { interval: '10s' }, - alertTypeId: 'myType', - enabled: false, - actions: [ - { - group: 'default', - id: '1', - actionTypeId: '1', - actionRef: '1', - params: { - foo: true, - }, - }, - ], - }, - version: '123', - references: [], - ...overrides, -}); diff --git a/x-pack/plugins/actions/server/authorization/get_authorization_mode_by_source.ts b/x-pack/plugins/actions/server/authorization/get_authorization_mode_by_source.ts deleted file mode 100644 index ace66798b24ba..0000000000000 --- a/x-pack/plugins/actions/server/authorization/get_authorization_mode_by_source.ts +++ /dev/null @@ -1,90 +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 { Logger, SavedObjectsClientContract } from '@kbn/core/server'; -import { ActionExecutionSource, isSavedObjectExecutionSource } from '../lib'; -import { ALERT_SAVED_OBJECT_TYPE } from '../constants/saved_objects'; -import { SavedObjectExecutionSource } from '../lib/action_execution_source'; - -const LEGACY_VERSION = 'pre-7.10.0'; - -export enum AuthorizationMode { - Legacy, - RBAC, -} - -export async function getAuthorizationModeBySource( - unsecuredSavedObjectsClient: SavedObjectsClientContract, - executionSource?: ActionExecutionSource -): Promise { - return isSavedObjectExecutionSource(executionSource) && - executionSource?.source?.type === ALERT_SAVED_OBJECT_TYPE && - ( - await unsecuredSavedObjectsClient.get<{ - meta?: { - versionApiKeyLastmodified?: string; - }; - }>(ALERT_SAVED_OBJECT_TYPE, executionSource.source.id) - ).attributes.meta?.versionApiKeyLastmodified === LEGACY_VERSION - ? AuthorizationMode.Legacy - : AuthorizationMode.RBAC; -} - -export async function bulkGetAuthorizationModeBySource( - logger: Logger, - unsecuredSavedObjectsClient: SavedObjectsClientContract, - executionSources: Array> = [] -): Promise> { - const authModes = { [AuthorizationMode.Legacy]: 0, [AuthorizationMode.RBAC]: 0 }; - - const alertSavedObjectExecutionSources: SavedObjectExecutionSource[] = executionSources.filter( - (source) => - isSavedObjectExecutionSource(source) && source?.source?.type === ALERT_SAVED_OBJECT_TYPE - ) as SavedObjectExecutionSource[]; - - // If no ALERT_SAVED_OBJECT_TYPE source, default to RBAC - if (alertSavedObjectExecutionSources.length === 0) { - authModes[AuthorizationMode.RBAC] = 1; - return authModes; - } - - // Collect the unique rule IDs for ALERT_SAVED_OBJECT_TYPE sources and bulk get the associated SOs - const rulesIds = new Set( - alertSavedObjectExecutionSources.map((source: SavedObjectExecutionSource) => source?.source?.id) - ); - - // Get rule saved objects to determine whether to use RBAC or legacy authorization source - const ruleSOs = await unsecuredSavedObjectsClient.bulkGet<{ - meta?: { - versionApiKeyLastmodified?: string; - }; - }>( - [...rulesIds].map((id) => ({ - type: ALERT_SAVED_OBJECT_TYPE, - id, - })) - ); - - return ruleSOs.saved_objects.reduce((acc, ruleSO) => { - if (ruleSO.error) { - logger.warn( - `Error retrieving saved object [${ruleSO.type}/${ruleSO.id}] - ${ruleSO.error?.message} - default to using RBAC authorization mode.` - ); - // If there's an error retrieving the saved object, default to RBAC auth mode to avoid privilege de-escalation - authModes[AuthorizationMode.RBAC]++; - } else { - // Check whether this is a legacy rule - const isLegacy = ruleSO.attributes?.meta?.versionApiKeyLastmodified === LEGACY_VERSION; - if (isLegacy) { - authModes[AuthorizationMode.Legacy]++; - } else { - authModes[AuthorizationMode.RBAC]++; - } - } - return acc; - }, authModes); -} diff --git a/x-pack/plugins/actions/server/config.test.ts b/x-pack/plugins/actions/server/config.test.ts index 5adc9c18b07a7..01bcd867fda53 100644 --- a/x-pack/plugins/actions/server/config.test.ts +++ b/x-pack/plugins/actions/server/config.test.ts @@ -35,9 +35,10 @@ describe('config validation', () => { "microsoftGraphApiUrl": "https://graph.microsoft.com/v1.0", "preconfigured": Object {}, "preconfiguredAlertHistoryEsIndex": false, - "proxyRejectUnauthorizedCertificates": true, - "rejectUnauthorized": true, "responseTimeout": "PT1M", + "usage": Object { + "url": "https://usage-api.usage-api/api/v1/usage", + }, } `); }); @@ -53,8 +54,6 @@ describe('config validation', () => { }, }, }, - proxyRejectUnauthorizedCertificates: false, - rejectUnauthorized: false, }; expect(configSchema.validate(config)).toMatchInlineSnapshot(` Object { @@ -82,9 +81,10 @@ describe('config validation', () => { }, }, "preconfiguredAlertHistoryEsIndex": false, - "proxyRejectUnauthorizedCertificates": false, - "rejectUnauthorized": false, "responseTimeout": "PT1M", + "usage": Object { + "url": "https://usage-api.usage-api/api/v1/usage", + }, } `); }); @@ -218,13 +218,14 @@ describe('config validation', () => { "microsoftGraphApiUrl": "https://graph.microsoft.com/v1.0", "preconfigured": Object {}, "preconfiguredAlertHistoryEsIndex": false, - "proxyRejectUnauthorizedCertificates": true, - "rejectUnauthorized": true, "responseTimeout": "PT1M", "ssl": Object { "proxyVerificationMode": "none", "verificationMode": "none", }, + "usage": Object { + "url": "https://usage-api.usage-api/api/v1/usage", + }, } `); }); diff --git a/x-pack/plugins/actions/server/config.ts b/x-pack/plugins/actions/server/config.ts index d806bde1fa227..f16a9830678cd 100644 --- a/x-pack/plugins/actions/server/config.ts +++ b/x-pack/plugins/actions/server/config.ts @@ -44,10 +44,6 @@ const customHostSettingsSchema = schema.object({ ), ssl: schema.maybe( schema.object({ - /** - * @deprecated in favor of `verificationMode` - **/ - rejectUnauthorized: schema.maybe(schema.boolean()), verificationMode: schema.maybe( schema.oneOf( [schema.literal('none'), schema.literal('certificate'), schema.literal('full')], @@ -72,6 +68,8 @@ const connectorTypeSchema = schema.object({ maxAttempts: schema.maybe(schema.number({ min: MIN_MAX_ATTEMPTS, max: MAX_MAX_ATTEMPTS })), }); +export const DEFAULT_USAGE_API_URL = 'https://usage-api.usage-api/api/v1/usage'; + // We leverage enabledActionTypes list by allowing the other plugins to overwrite it by using "setEnabledConnectorTypes" in the plugin setup. // The list can be overwritten only if it's not already been set in the config. const enabledConnectorTypesSchema = schema.arrayOf( @@ -96,16 +94,8 @@ export const configSchema = schema.object({ }), proxyUrl: schema.maybe(schema.string()), proxyHeaders: schema.maybe(schema.recordOf(schema.string(), schema.string())), - /** - * @deprecated in favor of `ssl.proxyVerificationMode` - **/ - proxyRejectUnauthorizedCertificates: schema.boolean({ defaultValue: true }), proxyBypassHosts: schema.maybe(schema.arrayOf(schema.string({ hostname: true }))), proxyOnlyHosts: schema.maybe(schema.arrayOf(schema.string({ hostname: true }))), - /** - * @deprecated in favor of `ssl.verificationMode` - **/ - rejectUnauthorized: schema.boolean({ defaultValue: true }), ssl: schema.maybe( schema.object({ verificationMode: schema.maybe( @@ -145,15 +135,14 @@ export const configSchema = schema.object({ max: schema.maybe(schema.number({ min: MIN_QUEUED_MAX, defaultValue: DEFAULT_QUEUED_MAX })), }) ), - usage: schema.maybe( - schema.object({ - ca: schema.maybe( - schema.object({ - path: schema.string(), - }) - ), - }) - ), + usage: schema.object({ + url: schema.string({ defaultValue: DEFAULT_USAGE_API_URL }), + ca: schema.maybe( + schema.object({ + path: schema.string(), + }) + ), + }), }); export type ActionsConfig = TypeOf; diff --git a/x-pack/plugins/actions/server/index.test.ts b/x-pack/plugins/actions/server/index.test.ts deleted file mode 100644 index 43f69da15de2f..0000000000000 --- a/x-pack/plugins/actions/server/index.test.ts +++ /dev/null @@ -1,61 +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 { config } from '.'; -import { applyDeprecations, configDeprecationFactory } from '@kbn/config'; -import { configDeprecationsMock } from '@kbn/core/server/mocks'; - -const CONFIG_PATH = 'xpack.actions'; -const applyStackAlertDeprecations = (settings: Record = {}) => { - const deprecations = config.deprecations!(configDeprecationFactory); - const deprecationMessages: string[] = []; - const _config = { - [CONFIG_PATH]: settings, - }; - const { config: migrated, changedPaths } = applyDeprecations( - _config, - deprecations.map((deprecation) => ({ - deprecation, - path: CONFIG_PATH, - context: configDeprecationsMock.createContext(), - })), - () => - ({ message }) => - deprecationMessages.push(message) - ); - return { - messages: deprecationMessages, - migrated, - changedPaths, - }; -}; - -describe('index', () => { - describe('deprecations', () => { - it('should properly unset deprecated configs', () => { - const { messages, changedPaths } = applyStackAlertDeprecations({ - customHostSettings: [{ ssl: { rejectUnauthorized: false } }], - rejectUnauthorized: false, - proxyRejectUnauthorizedCertificates: false, - }); - expect(changedPaths.unset).toStrictEqual([ - 'xpack.actions.customHostSettings.ssl.rejectUnauthorized', - 'xpack.actions.rejectUnauthorized', - 'xpack.actions.proxyRejectUnauthorizedCertificates', - ]); - expect(messages.length).toBe(3); - expect(messages[0]).toBe( - '"xpack.actions.customHostSettings[].ssl.rejectUnauthorized" is deprecated.Use "xpack.actions.customHostSettings[].ssl.verificationMode" instead, with the setting "verificationMode:full" eql to "rejectUnauthorized:true", and "verificationMode:none" eql to "rejectUnauthorized:false".' - ); - expect(messages[1]).toBe( - '"xpack.actions.rejectUnauthorized" is deprecated. Use "xpack.actions.ssl.verificationMode" instead, with the setting "verificationMode:full" eql to "rejectUnauthorized:true", and "verificationMode:none" eql to "rejectUnauthorized:false".' - ); - expect(messages[2]).toBe( - '"xpack.actions.proxyRejectUnauthorizedCertificates" is deprecated. Use "xpack.actions.ssl.proxyVerificationMode" instead, with the setting "proxyVerificationMode:full" eql to "rejectUnauthorized:true",and "proxyVerificationMode:none" eql to "rejectUnauthorized:false".' - ); - }); - }); -}); diff --git a/x-pack/plugins/actions/server/index.ts b/x-pack/plugins/actions/server/index.ts index 1d5aa22ba07cf..d391911ff0fc3 100644 --- a/x-pack/plugins/actions/server/index.ts +++ b/x-pack/plugins/actions/server/index.ts @@ -4,10 +4,9 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { get } from 'lodash'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { PluginInitializerContext, PluginConfigDescriptor } from '@kbn/core/server'; -import { configSchema, ActionsConfig, CustomHostSettings } from './config'; +import { configSchema, ActionsConfig } from './config'; import { ActionsClient as ActionsClientClass } from './actions_client'; import { ActionsAuthorization as ActionsAuthorizationClass } from './authorization/actions_authorization'; @@ -51,103 +50,6 @@ export const config: PluginConfigDescriptor = { exposeToBrowser: { email: { domain_allowlist: true }, }, - deprecations: ({ renameFromRoot, unused }) => [ - renameFromRoot('xpack.actions.whitelistedHosts', 'xpack.actions.allowedHosts', { - level: 'warning', - }), - (settings, fromPath, addDeprecation) => { - const actions = get(settings, fromPath); - const customHostSettings = actions?.customHostSettings ?? []; - if ( - customHostSettings.find( - (customHostSchema: CustomHostSettings) => - Object.hasOwn(customHostSchema, 'ssl') && - Object.hasOwn(customHostSchema.ssl ?? {}, 'rejectUnauthorized') - ) - ) { - addDeprecation({ - level: 'warning', - configPath: 'xpack.actions.customHostSettings.ssl.rejectUnauthorized', - message: - `"xpack.actions.customHostSettings[].ssl.rejectUnauthorized" is deprecated.` + - `Use "xpack.actions.customHostSettings[].ssl.verificationMode" instead, ` + - `with the setting "verificationMode:full" eql to "rejectUnauthorized:true", ` + - `and "verificationMode:none" eql to "rejectUnauthorized:false".`, - correctiveActions: { - manualSteps: [ - `Remove "xpack.actions.customHostSettings[].ssl.rejectUnauthorized" from your kibana configs.`, - `Use "xpack.actions.customHostSettings[].ssl.verificationMode" ` + - `with the setting "verificationMode:full" eql to "rejectUnauthorized:true", ` + - `and "verificationMode:none" eql to "rejectUnauthorized:false".`, - ], - }, - }); - return { - unset: [ - { - path: `xpack.actions.customHostSettings.ssl.rejectUnauthorized`, - }, - ], - }; - } - }, - (settings, fromPath, addDeprecation) => { - const actions = get(settings, fromPath); - if (Object.hasOwn(actions ?? {}, 'rejectUnauthorized')) { - addDeprecation({ - level: 'warning', - configPath: `${fromPath}.rejectUnauthorized`, - message: - `"xpack.actions.rejectUnauthorized" is deprecated. Use "xpack.actions.ssl.verificationMode" instead, ` + - `with the setting "verificationMode:full" eql to "rejectUnauthorized:true", ` + - `and "verificationMode:none" eql to "rejectUnauthorized:false".`, - correctiveActions: { - manualSteps: [ - `Remove "xpack.actions.rejectUnauthorized" from your kibana configs.`, - `Use "xpack.actions.ssl.verificationMode" ` + - `with the setting "verificationMode:full" eql to "rejectUnauthorized:true", ` + - `and "verificationMode:none" eql to "rejectUnauthorized:false".`, - ], - }, - }); - return { - unset: [ - { - path: `xpack.actions.rejectUnauthorized`, - }, - ], - }; - } - }, - (settings, fromPath, addDeprecation) => { - const actions = get(settings, fromPath); - if (Object.hasOwn(actions ?? {}, 'proxyRejectUnauthorizedCertificates')) { - addDeprecation({ - level: 'warning', - configPath: `${fromPath}.proxyRejectUnauthorizedCertificates`, - message: - `"xpack.actions.proxyRejectUnauthorizedCertificates" is deprecated. Use "xpack.actions.ssl.proxyVerificationMode" instead, ` + - `with the setting "proxyVerificationMode:full" eql to "rejectUnauthorized:true",` + - `and "proxyVerificationMode:none" eql to "rejectUnauthorized:false".`, - correctiveActions: { - manualSteps: [ - `Remove "xpack.actions.proxyRejectUnauthorizedCertificates" from your kibana configs.`, - `Use "xpack.actions.ssl.proxyVerificationMode" ` + - `with the setting "proxyVerificationMode:full" eql to "rejectUnauthorized:true",` + - `and "proxyVerificationMode:none" eql to "rejectUnauthorized:false".`, - ], - }, - }); - return { - unset: [ - { - path: `xpack.actions.proxyRejectUnauthorizedCertificates`, - }, - ], - }; - } - }, - ], }; export { urlAllowListValidator } from './sub_action_framework/helpers'; diff --git a/x-pack/plugins/actions/server/integration_tests/axios_utils_connection.test.ts b/x-pack/plugins/actions/server/integration_tests/axios_utils_connection.test.ts index 200656d339ac3..a0454cb2bbc18 100644 --- a/x-pack/plugins/actions/server/integration_tests/axios_utils_connection.test.ts +++ b/x-pack/plugins/actions/server/integration_tests/axios_utils_connection.test.ts @@ -20,7 +20,7 @@ import { ByteSizeValue } from '@kbn/config-schema'; import { Logger } from '@kbn/core/server'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { createReadySignal } from '@kbn/event-log-plugin/server/lib/ready_signal'; -import { ActionsConfig } from '../config'; +import { ActionsConfig, DEFAULT_USAGE_API_URL } from '../config'; import { ActionsConfigurationUtilities, getActionsConfigurationUtilities } from '../actions_config'; import { resolveCustomHosts } from '../lib/custom_host_settings'; import { @@ -461,7 +461,6 @@ async function rejectUnauthorizedTargetProxyTest(opts: RunTestOptions) { await runWithSetup(opts, async (target, proxyInstance, axiosDefaults) => { const acu = getACUfromConfig({ proxyUrl: proxyInstance.url, - rejectUnauthorized: false, customHostSettings: [{ url: target.url, ssl: { verificationMode: 'none' } }], }); @@ -676,14 +675,12 @@ const BaseActionsConfig: ActionsConfig = { preconfigured: {}, proxyUrl: undefined, proxyHeaders: undefined, - proxyRejectUnauthorizedCertificates: true, ssl: { proxyVerificationMode: 'full', verificationMode: 'full', }, proxyBypassHosts: undefined, proxyOnlyHosts: undefined, - rejectUnauthorized: true, maxResponseContentLength: ByteSizeValue.parse('1mb'), responseTimeout: momentDuration(1000 * 30), customHostSettings: undefined, @@ -691,6 +688,9 @@ const BaseActionsConfig: ActionsConfig = { microsoftGraphApiUrl: DEFAULT_MICROSOFT_GRAPH_API_URL, microsoftGraphApiScope: DEFAULT_MICROSOFT_GRAPH_API_SCOPE, microsoftExchangeUrl: DEFAULT_MICROSOFT_EXCHANGE_URL, + usage: { + url: DEFAULT_USAGE_API_URL, + }, }; function getACUfromConfig(config: Partial = {}): ActionsConfigurationUtilities { diff --git a/x-pack/plugins/actions/server/integration_tests/axios_utils_proxy.test.ts b/x-pack/plugins/actions/server/integration_tests/axios_utils_proxy.test.ts index f29b2a9855186..97a917d6b6893 100644 --- a/x-pack/plugins/actions/server/integration_tests/axios_utils_proxy.test.ts +++ b/x-pack/plugins/actions/server/integration_tests/axios_utils_proxy.test.ts @@ -20,7 +20,7 @@ import { ByteSizeValue } from '@kbn/config-schema'; import { Logger } from '@kbn/core/server'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { createReadySignal } from '@kbn/event-log-plugin/server/lib/ready_signal'; -import { ActionsConfig } from '../config'; +import { ActionsConfig, DEFAULT_USAGE_API_URL } from '../config'; import { ActionsConfigurationUtilities, getActionsConfigurationUtilities } from '../actions_config'; import { resolveCustomHosts } from '../lib/custom_host_settings'; import { @@ -366,7 +366,6 @@ async function rejectUnauthorizedTargetProxyTest(opts: RunTestOptions) { await runWithSetup(opts, async (target, proxyInstance, axiosDefaults) => { const acu = getACUfromConfig({ proxyUrl: proxyInstance.url, - rejectUnauthorized: false, customHostSettings: [{ url: target.url, ssl: { verificationMode: 'none' } }], }); @@ -582,14 +581,12 @@ const BaseActionsConfig: ActionsConfig = { preconfigured: {}, proxyUrl: undefined, proxyHeaders: undefined, - proxyRejectUnauthorizedCertificates: true, ssl: { proxyVerificationMode: 'full', verificationMode: 'full', }, proxyBypassHosts: undefined, proxyOnlyHosts: undefined, - rejectUnauthorized: true, maxResponseContentLength: ByteSizeValue.parse('1mb'), responseTimeout: momentDuration(1000 * 30), customHostSettings: undefined, @@ -597,6 +594,9 @@ const BaseActionsConfig: ActionsConfig = { microsoftGraphApiUrl: DEFAULT_MICROSOFT_GRAPH_API_URL, microsoftGraphApiScope: DEFAULT_MICROSOFT_GRAPH_API_SCOPE, microsoftExchangeUrl: DEFAULT_MICROSOFT_EXCHANGE_URL, + usage: { + url: DEFAULT_USAGE_API_URL, + }, }; function getACUfromConfig(config: Partial = {}): ActionsConfigurationUtilities { diff --git a/x-pack/plugins/actions/server/lib/custom_host_settings.test.ts b/x-pack/plugins/actions/server/lib/custom_host_settings.test.ts index 818d7fb9bcd0a..f9173f5e007d0 100644 --- a/x-pack/plugins/actions/server/lib/custom_host_settings.test.ts +++ b/x-pack/plugins/actions/server/lib/custom_host_settings.test.ts @@ -10,7 +10,7 @@ import { resolve as pathResolve, join as pathJoin } from 'path'; import { ByteSizeValue } from '@kbn/config-schema'; import moment from 'moment'; -import { ActionsConfig } from '../config'; +import { ActionsConfig, DEFAULT_USAGE_API_URL } from '../config'; import { Logger } from '@kbn/core/server'; import { loggingSystemMock } from '@kbn/core/server/mocks'; @@ -74,14 +74,15 @@ describe('custom_host_settings', () => { enabledActionTypes: [], preconfiguredAlertHistoryEsIndex: false, preconfigured: {}, - proxyRejectUnauthorizedCertificates: true, - rejectUnauthorized: true, maxResponseContentLength: new ByteSizeValue(1000000), responseTimeout: moment.duration(60000), enableFooterInEmail: true, microsoftGraphApiUrl: DEFAULT_MICROSOFT_GRAPH_API_URL, microsoftGraphApiScope: DEFAULT_MICROSOFT_GRAPH_API_SCOPE, microsoftExchangeUrl: DEFAULT_MICROSOFT_EXCHANGE_URL, + usage: { + url: DEFAULT_USAGE_API_URL, + }, }; test('ensure it copies over the config parts that it does not touch', () => { @@ -116,14 +117,12 @@ describe('custom_host_settings', () => { url: 'https://elastic.co:443', ssl: { certificateAuthoritiesData: 'xyz', - rejectUnauthorized: false, }, }, { url: 'smtp://mail.elastic.com:25', ssl: { certificateAuthoritiesData: 'abc', - rejectUnauthorized: true, }, smtp: { ignoreTLS: true, @@ -470,15 +469,9 @@ describe('custom_host_settings', () => { customHostSettings: [ { url: 'https://almost.purrfect.com/', - ssl: { - rejectUnauthorized: true, - }, }, { url: 'https://almost.purrfect.com:443', - ssl: { - rejectUnauthorized: false, - }, }, ], }; @@ -488,9 +481,6 @@ describe('custom_host_settings', () => { customHostSettings: [ { url: 'https://almost.purrfect.com:443', - ssl: { - rejectUnauthorized: true, - }, }, ], }; diff --git a/x-pack/plugins/actions/server/lib/get_custom_agents.ts b/x-pack/plugins/actions/server/lib/get_custom_agents.ts index 26b1495902eb1..c433bec18a54b 100644 --- a/x-pack/plugins/actions/server/lib/get_custom_agents.ts +++ b/x-pack/plugins/actions/server/lib/get_custom_agents.ts @@ -59,10 +59,7 @@ export function getCustomAgents( agentOptions.ca = sslSettings.certificateAuthoritiesData; } - const sslSettingsFromConfig = getSSLSettingsFromConfig( - sslSettings.verificationMode, - sslSettings.rejectUnauthorized - ); + const sslSettingsFromConfig = getSSLSettingsFromConfig(sslSettings.verificationMode); // see: src/core/server/elasticsearch/legacy/elasticsearch_client_config.ts // This is where the global rejectUnauthorized is overridden by a custom host const customHostNodeSSLOptions = getNodeSSLOptions( diff --git a/x-pack/plugins/actions/server/lib/track_legacy_rbac_exemption.test.ts b/x-pack/plugins/actions/server/lib/track_legacy_rbac_exemption.test.ts deleted file mode 100644 index 6d44a5d982e62..0000000000000 --- a/x-pack/plugins/actions/server/lib/track_legacy_rbac_exemption.test.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 { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; -import { trackLegacyRBACExemption } from './track_legacy_rbac_exemption'; - -describe('trackLegacyRBACExemption', () => { - it('should call `usageCounter.incrementCounter`', () => { - const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); - const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); - - trackLegacyRBACExemption('test', mockUsageCounter); - expect(mockUsageCounter.incrementCounter).toHaveBeenCalledWith({ - counterName: `source_test`, - counterType: 'legacyRBACExemption', - incrementBy: 1, - }); - }); - - it('should do nothing if no usage counter is provided', () => { - let err; - try { - trackLegacyRBACExemption('test', undefined); - } catch (e) { - err = e; - } - expect(err).toBeUndefined(); - }); - - it('should call `usageCounter.incrementCounter` and increment by the passed in value', () => { - const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); - const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); - - trackLegacyRBACExemption('test', mockUsageCounter, 15); - expect(mockUsageCounter.incrementCounter).toHaveBeenCalledWith({ - counterName: `source_test`, - counterType: 'legacyRBACExemption', - incrementBy: 15, - }); - }); -}); diff --git a/x-pack/plugins/actions/server/lib/track_legacy_rbac_exemption.ts b/x-pack/plugins/actions/server/lib/track_legacy_rbac_exemption.ts deleted file mode 100644 index b6f1af3d9de76..0000000000000 --- a/x-pack/plugins/actions/server/lib/track_legacy_rbac_exemption.ts +++ /dev/null @@ -1,22 +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 { UsageCounter } from '@kbn/usage-collection-plugin/server'; - -export function trackLegacyRBACExemption( - source: string, - usageCounter?: UsageCounter, - increment?: number -) { - if (usageCounter) { - usageCounter.incrementCounter({ - counterName: `source_${source}`, - counterType: 'legacyRBACExemption', - incrementBy: increment ? increment : 1, - }); - } -} diff --git a/x-pack/plugins/actions/server/plugin.test.ts b/x-pack/plugins/actions/server/plugin.test.ts index 89efb80867fd7..f5de52810b162 100644 --- a/x-pack/plugins/actions/server/plugin.test.ts +++ b/x-pack/plugins/actions/server/plugin.test.ts @@ -30,6 +30,7 @@ import { DEFAULT_MICROSOFT_GRAPH_API_SCOPE, DEFAULT_MICROSOFT_GRAPH_API_URL, } from '../common'; +import { cloudMock } from '@kbn/cloud-plugin/server/mocks'; const executor: ExecutorType<{}, {}, {}, void> = async (options) => { return { status: 'ok', actionId: options.actionId }; @@ -49,16 +50,17 @@ function getConfig(overrides = {}) { secrets: {}, }, }, - proxyRejectUnauthorizedCertificates: true, proxyBypassHosts: undefined, proxyOnlyHosts: undefined, - rejectUnauthorized: true, maxResponseContentLength: new ByteSizeValue(1000000), responseTimeout: moment.duration('60s'), enableFooterInEmail: true, microsoftGraphApiUrl: DEFAULT_MICROSOFT_GRAPH_API_URL, microsoftGraphApiScope: DEFAULT_MICROSOFT_GRAPH_API_SCOPE, microsoftExchangeUrl: DEFAULT_MICROSOFT_EXCHANGE_URL, + usage: { + url: 'ca.path', + }, ...overrides, }; } @@ -76,14 +78,15 @@ describe('Actions Plugin', () => { allowedHosts: ['*'], preconfiguredAlertHistoryEsIndex: false, preconfigured: {}, - proxyRejectUnauthorizedCertificates: true, - rejectUnauthorized: true, maxResponseContentLength: new ByteSizeValue(1000000), responseTimeout: moment.duration(60000), enableFooterInEmail: true, microsoftGraphApiUrl: DEFAULT_MICROSOFT_GRAPH_API_URL, microsoftGraphApiScope: DEFAULT_MICROSOFT_GRAPH_API_SCOPE, microsoftExchangeUrl: DEFAULT_MICROSOFT_EXCHANGE_URL, + usage: { + url: 'ca.path', + }, }); plugin = new ActionsPlugin(context); coreSetup = coreMock.createSetup(); @@ -95,6 +98,7 @@ describe('Actions Plugin', () => { eventLog: eventLogMock.createSetup(), usageCollection: usageCollectionPluginMock.createSetupContract(), features: featuresPluginMock.createSetup(), + cloud: cloudMock.createSetup(), }; coreSetup.getStartServices.mockResolvedValue([ coreMock.createStart(), @@ -347,6 +351,7 @@ describe('Actions Plugin', () => { eventLog: eventLogMock.createSetup(), usageCollection: usageCollectionPluginMock.createSetupContract(), features: featuresPluginMock.createSetup(), + cloud: cloudMock.createSetup(), }; } @@ -374,6 +379,7 @@ describe('Actions Plugin', () => { usageCollection: usageCollectionPluginMock.createSetupContract(), features: featuresPluginMock.createSetup(), serverless: serverlessPluginMock.createSetupContract(), + cloud: cloudMock.createSetup(), }; } @@ -577,14 +583,15 @@ describe('Actions Plugin', () => { secrets: {}, }, }, - proxyRejectUnauthorizedCertificates: true, - rejectUnauthorized: true, maxResponseContentLength: new ByteSizeValue(1000000), responseTimeout: moment.duration(60000), enableFooterInEmail: true, microsoftGraphApiUrl: DEFAULT_MICROSOFT_GRAPH_API_URL, microsoftGraphApiScope: DEFAULT_MICROSOFT_GRAPH_API_SCOPE, microsoftExchangeUrl: DEFAULT_MICROSOFT_EXCHANGE_URL, + usage: { + url: 'ca.path', + }, }); plugin = new ActionsPlugin(context); coreSetup = coreMock.createSetup(); @@ -596,6 +603,7 @@ describe('Actions Plugin', () => { eventLog: eventLogMock.createSetup(), usageCollection: usageCollectionPluginMock.createSetupContract(), features: featuresPluginMock.createSetup(), + cloud: cloudMock.createSetup(), }; pluginsStart = { licensing: licensingMock.createStart(), @@ -680,6 +688,7 @@ describe('Actions Plugin', () => { eventLog: eventLogMock.createSetup(), usageCollection: usageCollectionPluginMock.createSetupContract(), features: featuresPluginMock.createSetup(), + cloud: cloudMock.createSetup(), }; pluginsStart = { licensing: licensingMock.createStart(), diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 00dc17c2f92d7..57304c176c13d 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -42,6 +42,7 @@ import { import { MonitoringCollectionSetup } from '@kbn/monitoring-collection-plugin/server'; import { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/server'; +import type { CloudSetup } from '@kbn/cloud-plugin/server'; import { ActionsConfig, AllowedHosts, EnabledConnectorTypes, getValidatedConfig } from './config'; import { resolveCustomHosts } from './lib/custom_host_settings'; import { events } from './lib/event_based_telemetry'; @@ -84,10 +85,6 @@ import { setupSavedObjects } from './saved_objects'; import { ACTIONS_FEATURE } from './feature'; import { ActionsAuthorization } from './authorization/actions_authorization'; import { ActionExecutionSource } from './lib/action_execution_source'; -import { - getAuthorizationModeBySource, - AuthorizationMode, -} from './authorization/get_authorization_mode_by_source'; import { ensureSufficientLicense } from './lib/ensure_sufficient_license'; import { renderMustacheObject } from './lib/mustache_renderer'; import { getAlertHistoryEsIndex } from './preconfigured_connectors/alert_history_es_index/alert_history_es_index'; @@ -112,6 +109,7 @@ import type { IUnsecuredActionsClient } from './unsecured_actions_client/unsecur import { UnsecuredActionsClient } from './unsecured_actions_client/unsecured_actions_client'; import { createBulkUnsecuredExecutionEnqueuerFunction } from './create_unsecured_execute_function'; import { createSystemConnectors } from './create_system_actions'; +import { ConnectorUsageReportingTask } from './usage/connector_usage_reporting_task'; export interface PluginSetupContract { registerType< @@ -184,6 +182,7 @@ export interface ActionsPluginsSetup { spaces?: SpacesPluginSetup; monitoringCollection?: MonitoringCollectionSetup; serverless?: ServerlessPluginSetup; + cloud: CloudSetup; } export interface ActionsPluginsStart { @@ -218,6 +217,7 @@ export class ActionsPlugin implements Plugin getActionsClientWithRequest(request); @@ -603,6 +606,8 @@ export class ActionsPlugin implements Plugin {}); + return { isActionTypeEnabled: (id, options = { notifyUsage: false }) => { return this.actionTypeRegistry!.isActionTypeEnabled(id, options); @@ -641,13 +646,9 @@ export class ActionsPlugin implements Plugin { + private instantiateAuthorization = (request: KibanaRequest) => { return new ActionsAuthorization({ request, - authorizationMode, authorization: this.security?.authz, }); }; diff --git a/x-pack/plugins/actions/server/routes/legacy/_mock_handler_arguments.ts b/x-pack/plugins/actions/server/routes/_mock_handler_arguments.ts similarity index 87% rename from x-pack/plugins/actions/server/routes/legacy/_mock_handler_arguments.ts rename to x-pack/plugins/actions/server/routes/_mock_handler_arguments.ts index 73906fa0a63e3..fc2610b804b69 100644 --- a/x-pack/plugins/actions/server/routes/legacy/_mock_handler_arguments.ts +++ b/x-pack/plugins/actions/server/routes/_mock_handler_arguments.ts @@ -9,10 +9,10 @@ import { KibanaRequest, KibanaResponseFactory } from '@kbn/core/server'; import { identity } from 'lodash'; import type { MethodKeysOf } from '@kbn/utility-types'; import { httpServerMock } from '@kbn/core/server/mocks'; -import { ActionsRequestHandlerContext } from '../../types'; -import { actionsClientMock } from '../../mocks'; -import { ActionsClientMock } from '../../actions_client/actions_client.mock'; -import { ConnectorType } from '../../application/connector/types'; +import { ActionsRequestHandlerContext } from '../types'; +import { actionsClientMock } from '../mocks'; +import { ActionsClientMock } from '../actions_client/actions_client.mock'; +import { ConnectorType } from '../application/connector/types'; export function mockHandlerArguments( { diff --git a/x-pack/plugins/actions/server/routes/connector/create/create.test.ts b/x-pack/plugins/actions/server/routes/connector/create/create.test.ts index 0f97015bd4f01..7bf91629023c8 100644 --- a/x-pack/plugins/actions/server/routes/connector/create/create.test.ts +++ b/x-pack/plugins/actions/server/routes/connector/create/create.test.ts @@ -8,7 +8,7 @@ import { createConnectorRoute } from './create'; import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../../../lib/license_state.mock'; -import { mockHandlerArguments } from '../../legacy/_mock_handler_arguments'; +import { mockHandlerArguments } from '../../_mock_handler_arguments'; import { verifyAccessAndContext } from '../../verify_access_and_context'; import { omit } from 'lodash'; import { actionsClientMock } from '../../../actions_client/actions_client.mock'; diff --git a/x-pack/plugins/actions/server/routes/connector/delete/delete.test.ts b/x-pack/plugins/actions/server/routes/connector/delete/delete.test.ts index 9fb3f7f3a8ae5..82e6a6584a641 100644 --- a/x-pack/plugins/actions/server/routes/connector/delete/delete.test.ts +++ b/x-pack/plugins/actions/server/routes/connector/delete/delete.test.ts @@ -8,7 +8,7 @@ import { deleteConnectorRoute } from './delete'; import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../../../lib/license_state.mock'; -import { mockHandlerArguments } from '../../legacy/_mock_handler_arguments'; +import { mockHandlerArguments } from '../../_mock_handler_arguments'; import { actionsClientMock } from '../../../mocks'; import { verifyAccessAndContext } from '../../verify_access_and_context'; diff --git a/x-pack/plugins/actions/server/routes/connector/execute/execute.test.ts b/x-pack/plugins/actions/server/routes/connector/execute/execute.test.ts index a9ae5e881f141..a0f5bf0629aec 100644 --- a/x-pack/plugins/actions/server/routes/connector/execute/execute.test.ts +++ b/x-pack/plugins/actions/server/routes/connector/execute/execute.test.ts @@ -8,7 +8,7 @@ import { executeConnectorRoute } from './execute'; import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../../../lib/license_state.mock'; -import { mockHandlerArguments } from '../../legacy/_mock_handler_arguments'; +import { mockHandlerArguments } from '../../_mock_handler_arguments'; import { asHttpRequestExecutionSource } from '../../../lib'; import { actionsClientMock } from '../../../actions_client/actions_client.mock'; import { ActionTypeExecutorResult } from '../../../types'; diff --git a/x-pack/plugins/actions/server/routes/connector/get/get.test.ts b/x-pack/plugins/actions/server/routes/connector/get/get.test.ts index 28293ae7947f2..cbf0cd86f9912 100644 --- a/x-pack/plugins/actions/server/routes/connector/get/get.test.ts +++ b/x-pack/plugins/actions/server/routes/connector/get/get.test.ts @@ -8,7 +8,7 @@ import { getConnectorRoute } from './get'; import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../../../lib/license_state.mock'; -import { mockHandlerArguments } from '../../legacy/_mock_handler_arguments'; +import { mockHandlerArguments } from '../../_mock_handler_arguments'; import { actionsClientMock } from '../../../actions_client/actions_client.mock'; import { verifyAccessAndContext } from '../../verify_access_and_context'; diff --git a/x-pack/plugins/actions/server/routes/connector/get_all/get_all.test.ts b/x-pack/plugins/actions/server/routes/connector/get_all/get_all.test.ts index 0ab3b57e238cf..5328cd76e2c4d 100644 --- a/x-pack/plugins/actions/server/routes/connector/get_all/get_all.test.ts +++ b/x-pack/plugins/actions/server/routes/connector/get_all/get_all.test.ts @@ -8,7 +8,7 @@ import { getAllConnectorsRoute } from './get_all'; import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../../../lib/license_state.mock'; -import { mockHandlerArguments } from '../../legacy/_mock_handler_arguments'; +import { mockHandlerArguments } from '../../_mock_handler_arguments'; import { verifyAccessAndContext } from '../../verify_access_and_context'; import { actionsClientMock } from '../../../actions_client/actions_client.mock'; diff --git a/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts index 07221aacddde7..a82eeef55ddda 100644 --- a/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts +++ b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts @@ -8,7 +8,7 @@ import { getAllConnectorsIncludingSystemRoute } from './get_all_system'; import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../../../lib/license_state.mock'; -import { mockHandlerArguments } from '../../legacy/_mock_handler_arguments'; +import { mockHandlerArguments } from '../../_mock_handler_arguments'; import { verifyAccessAndContext } from '../../verify_access_and_context'; import { actionsClientMock } from '../../../actions_client/actions_client.mock'; diff --git a/x-pack/plugins/actions/server/routes/connector/list_types/list_types.test.ts b/x-pack/plugins/actions/server/routes/connector/list_types/list_types.test.ts index e7370c7638a89..bf1ab91c5b6ab 100644 --- a/x-pack/plugins/actions/server/routes/connector/list_types/list_types.test.ts +++ b/x-pack/plugins/actions/server/routes/connector/list_types/list_types.test.ts @@ -8,7 +8,7 @@ import { httpServiceMock } from '@kbn/core/server/mocks'; import { LicenseType } from '@kbn/licensing-plugin/server'; import { licenseStateMock } from '../../../lib/license_state.mock'; -import { mockHandlerArguments } from '../../legacy/_mock_handler_arguments'; +import { mockHandlerArguments } from '../../_mock_handler_arguments'; import { listTypesRoute } from './list_types'; import { verifyAccessAndContext } from '../../verify_access_and_context'; import { actionsClientMock } from '../../../mocks'; diff --git a/x-pack/plugins/actions/server/routes/connector/list_types_system/list_types_system.test.ts b/x-pack/plugins/actions/server/routes/connector/list_types_system/list_types_system.test.ts index 07d2d3adcd4f3..7398d020f5972 100644 --- a/x-pack/plugins/actions/server/routes/connector/list_types_system/list_types_system.test.ts +++ b/x-pack/plugins/actions/server/routes/connector/list_types_system/list_types_system.test.ts @@ -8,7 +8,7 @@ import { httpServiceMock } from '@kbn/core/server/mocks'; import { LicenseType } from '@kbn/licensing-plugin/server'; import { licenseStateMock } from '../../../lib/license_state.mock'; -import { mockHandlerArguments } from '../../legacy/_mock_handler_arguments'; +import { mockHandlerArguments } from '../../_mock_handler_arguments'; import { listTypesWithSystemRoute } from './list_types_system'; import { verifyAccessAndContext } from '../../verify_access_and_context'; import { actionsClientMock } from '../../../mocks'; diff --git a/x-pack/plugins/actions/server/routes/connector/update/update.test.ts b/x-pack/plugins/actions/server/routes/connector/update/update.test.ts index f48c87fca43c2..870882513c5ae 100644 --- a/x-pack/plugins/actions/server/routes/connector/update/update.test.ts +++ b/x-pack/plugins/actions/server/routes/connector/update/update.test.ts @@ -8,7 +8,7 @@ import { updateConnectorRoute } from './update'; import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../../../lib/license_state.mock'; -import { mockHandlerArguments } from '../../legacy/_mock_handler_arguments'; +import { mockHandlerArguments } from '../../_mock_handler_arguments'; import { actionsClientMock } from '../../../actions_client/actions_client.mock'; import { verifyAccessAndContext } from '../../verify_access_and_context'; import { updateConnectorBodySchema } from '../../../../common/routes/connector/apis/update'; diff --git a/x-pack/plugins/actions/server/routes/get_global_execution_kpi.test.ts b/x-pack/plugins/actions/server/routes/get_global_execution_kpi.test.ts index 066d558bcfd59..026a53caebe48 100644 --- a/x-pack/plugins/actions/server/routes/get_global_execution_kpi.test.ts +++ b/x-pack/plugins/actions/server/routes/get_global_execution_kpi.test.ts @@ -7,7 +7,7 @@ import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../lib/license_state.mock'; -import { mockHandlerArguments } from './legacy/_mock_handler_arguments'; +import { mockHandlerArguments } from './_mock_handler_arguments'; import { actionsClientMock } from '../actions_client/actions_client.mock'; import { getGlobalExecutionKPIRoute } from './get_global_execution_kpi'; import { verifyAccessAndContext } from './verify_access_and_context'; diff --git a/x-pack/plugins/actions/server/routes/get_global_execution_logs.test.ts b/x-pack/plugins/actions/server/routes/get_global_execution_logs.test.ts index 4654885a49bcb..c31dedb52bb9d 100644 --- a/x-pack/plugins/actions/server/routes/get_global_execution_logs.test.ts +++ b/x-pack/plugins/actions/server/routes/get_global_execution_logs.test.ts @@ -8,7 +8,7 @@ import { getGlobalExecutionLogRoute } from './get_global_execution_logs'; import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../lib/license_state.mock'; -import { mockHandlerArguments } from './legacy/_mock_handler_arguments'; +import { mockHandlerArguments } from './_mock_handler_arguments'; import { actionsClientMock } from '../actions_client/actions_client.mock'; import { IExecutionLogResult } from '../../common'; import { verifyAccessAndContext } from './verify_access_and_context'; diff --git a/x-pack/plugins/actions/server/routes/get_oauth_access_token.test.ts b/x-pack/plugins/actions/server/routes/get_oauth_access_token.test.ts index c1b7dd26aff15..e3474ebcaa0a6 100644 --- a/x-pack/plugins/actions/server/routes/get_oauth_access_token.test.ts +++ b/x-pack/plugins/actions/server/routes/get_oauth_access_token.test.ts @@ -8,7 +8,7 @@ import { getOAuthAccessToken } from './get_oauth_access_token'; import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../lib/license_state.mock'; -import { mockHandlerArguments } from './legacy/_mock_handler_arguments'; +import { mockHandlerArguments } from './_mock_handler_arguments'; import { verifyAccessAndContext } from './verify_access_and_context'; import { actionsConfigMock } from '../actions_config.mock'; import { actionsClientMock } from '../actions_client/actions_client.mock'; diff --git a/x-pack/plugins/actions/server/routes/index.ts b/x-pack/plugins/actions/server/routes/index.ts index 39efe6619176b..7e6d70148f4fb 100644 --- a/x-pack/plugins/actions/server/routes/index.ts +++ b/x-pack/plugins/actions/server/routes/index.ts @@ -19,7 +19,6 @@ import { executeConnectorRoute } from './connector/execute'; import { getConnectorRoute } from './connector/get'; import { updateConnectorRoute } from './connector/update'; import { getOAuthAccessToken } from './get_oauth_access_token'; -import { defineLegacyRoutes } from './legacy'; import { ActionsConfigurationUtilities } from '../actions_config'; import { getGlobalExecutionLogRoute } from './get_global_execution_logs'; import { getGlobalExecutionKPIRoute } from './get_global_execution_kpi'; @@ -32,9 +31,7 @@ export interface RouteOptions { } export function defineRoutes(opts: RouteOptions) { - const { router, licenseState, actionsConfigUtils, usageCounter } = opts; - - defineLegacyRoutes(router, licenseState, usageCounter); + const { router, licenseState, actionsConfigUtils } = opts; createConnectorRoute(router, licenseState); deleteConnectorRoute(router, licenseState); diff --git a/x-pack/plugins/actions/server/routes/legacy/create.test.ts b/x-pack/plugins/actions/server/routes/legacy/create.test.ts deleted file mode 100644 index 05993e44746f9..0000000000000 --- a/x-pack/plugins/actions/server/routes/legacy/create.test.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 { createActionRoute } from './create'; -import { httpServiceMock } from '@kbn/core/server/mocks'; -import { licenseStateMock } from '../../lib/license_state.mock'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { actionsClientMock } from '../../actions_client/actions_client.mock'; -import { verifyAccessAndContext } from '../verify_access_and_context'; -import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; -import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; - -jest.mock('../verify_access_and_context', () => ({ - verifyAccessAndContext: jest.fn(), -})); - -jest.mock('../../lib/track_legacy_route_usage', () => ({ - trackLegacyRouteUsage: jest.fn(), -})); - -const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); -const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); - -beforeEach(() => { - jest.resetAllMocks(); - (verifyAccessAndContext as jest.Mock).mockImplementation((license, handler) => handler); -}); - -describe('createActionRoute', () => { - it('creates an action with proper parameters', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - createActionRoute(router, licenseState); - - const [config, handler] = router.post.mock.calls[0]; - - expect(config.path).toMatchInlineSnapshot(`"/api/actions/action"`); - - const createResult = { - id: '1', - name: 'My name', - actionTypeId: 'abc', - config: { foo: true }, - isPreconfigured: false, - isDeprecated: false, - isSystemAction: false, - }; - - const actionsClient = actionsClientMock.create(); - actionsClient.create.mockResolvedValueOnce(createResult); - - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { - body: { - name: 'My name', - actionTypeId: 'abc', - config: { foo: true }, - secrets: {}, - }, - }, - ['ok'] - ); - - expect(await handler(context, req, res)).toEqual({ body: createResult }); - - expect(actionsClient.create).toHaveBeenCalledTimes(1); - expect(actionsClient.create.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "action": Object { - "actionTypeId": "abc", - "config": Object { - "foo": true, - }, - "name": "My name", - "secrets": Object {}, - }, - }, - ] - `); - - expect(res.ok).toHaveBeenCalledWith({ - body: createResult, - }); - }); - - it('ensures the license allows creating actions', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - createActionRoute(router, licenseState); - - const [, handler] = router.post.mock.calls[0]; - - const actionsClient = actionsClientMock.create(); - actionsClient.create.mockResolvedValueOnce({ - id: '1', - name: 'My name', - actionTypeId: 'abc', - config: { foo: true }, - isPreconfigured: false, - isDeprecated: false, - isSystemAction: false, - }); - - const [context, req, res] = mockHandlerArguments({ actionsClient }, {}); - - await handler(context, req, res); - - expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function)); - }); - - it('ensures the license check prevents creating actions', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - (verifyAccessAndContext as jest.Mock).mockImplementation(() => async () => { - throw new Error('OMG'); - }); - - createActionRoute(router, licenseState); - - const [, handler] = router.post.mock.calls[0]; - - const actionsClient = actionsClientMock.create(); - actionsClient.create.mockResolvedValueOnce({ - id: '1', - name: 'My name', - actionTypeId: 'abc', - config: { foo: true }, - isPreconfigured: false, - isDeprecated: false, - isSystemAction: false, - }); - - const [context, req, res] = mockHandlerArguments({ actionsClient }, {}); - - await expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); - }); - - it('should track every call', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - const actionsClient = actionsClientMock.create(); - - createActionRoute(router, licenseState, mockUsageCounter); - const [, handler] = router.post.mock.calls[0]; - const [context, req, res] = mockHandlerArguments({ actionsClient }, {}); - await handler(context, req, res); - expect(trackLegacyRouteUsage).toHaveBeenCalledWith('create', mockUsageCounter); - }); -}); diff --git a/x-pack/plugins/actions/server/routes/legacy/create.ts b/x-pack/plugins/actions/server/routes/legacy/create.ts deleted file mode 100644 index f667a9e003a77..0000000000000 --- a/x-pack/plugins/actions/server/routes/legacy/create.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 { schema } from '@kbn/config-schema'; -import { UsageCounter } from '@kbn/usage-collection-plugin/server'; -import { IRouter } from '@kbn/core/server'; -import { ActionsRequestHandlerContext } from '../../types'; -import { ILicenseState } from '../../lib'; -import { BASE_ACTION_API_PATH } from '../../../common'; -import { verifyAccessAndContext } from '../verify_access_and_context'; -import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; -import { connectorResponseSchemaV1 } from '../../../common/routes/connector/response'; - -export const bodySchema = schema.object({ - name: schema.string({ - meta: { description: 'The display name for the connector.' }, - }), - actionTypeId: schema.string({ - meta: { description: 'The connector type identifier.' }, - }), - config: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), - secrets: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), -}); - -export const createActionRoute = ( - router: IRouter, - licenseState: ILicenseState, - usageCounter?: UsageCounter -) => { - router.post( - { - path: `${BASE_ACTION_API_PATH}/action`, - options: { - access: 'public', - summary: `Create a connector`, - tags: ['oas-tag:connectors'], - // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} - deprecated: true, - }, - validate: { - request: { - body: bodySchema, - }, - response: { - 200: { - description: 'Indicates a successful call.', - body: () => connectorResponseSchemaV1, - }, - }, - }, - }, - router.handleLegacyErrors( - verifyAccessAndContext(licenseState, async function (context, req, res) { - const actionsClient = (await context.actions).getActionsClient(); - const action = req.body; - trackLegacyRouteUsage('create', usageCounter); - return res.ok({ - body: await actionsClient.create({ action }), - }); - }) - ) - ); -}; diff --git a/x-pack/plugins/actions/server/routes/legacy/delete.test.ts b/x-pack/plugins/actions/server/routes/legacy/delete.test.ts deleted file mode 100644 index 2bfb5c7810e46..0000000000000 --- a/x-pack/plugins/actions/server/routes/legacy/delete.test.ts +++ /dev/null @@ -1,138 +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 { deleteActionRoute } from './delete'; -import { httpServiceMock } from '@kbn/core/server/mocks'; -import { licenseStateMock } from '../../lib/license_state.mock'; -import { verifyApiAccess } from '../../lib'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { actionsClientMock } from '../../mocks'; -import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; -import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; - -jest.mock('../../lib/verify_api_access', () => ({ - verifyApiAccess: jest.fn(), -})); - -jest.mock('../../lib/track_legacy_route_usage', () => ({ - trackLegacyRouteUsage: jest.fn(), -})); - -const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); -const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); - -beforeEach(() => { - jest.resetAllMocks(); -}); - -describe('deleteActionRoute', () => { - it('deletes an action with proper parameters', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - deleteActionRoute(router, licenseState); - - const [config, handler] = router.delete.mock.calls[0]; - - expect(config.path).toMatchInlineSnapshot(`"/api/actions/action/{id}"`); - - const actionsClient = actionsClientMock.create(); - actionsClient.delete.mockResolvedValueOnce({}); - - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { - params: { - id: '1', - }, - }, - ['noContent'] - ); - - expect(await handler(context, req, res)).toEqual(undefined); - - expect(actionsClient.delete).toHaveBeenCalledTimes(1); - expect(actionsClient.delete.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - }, - ] - `); - - expect(res.noContent).toHaveBeenCalled(); - }); - - it('ensures the license allows deleting actions', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - deleteActionRoute(router, licenseState); - - const [, handler] = router.delete.mock.calls[0]; - - const actionsClient = actionsClientMock.create(); - actionsClient.delete.mockResolvedValueOnce({}); - - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { - params: { id: '1' }, - } - ); - - await handler(context, req, res); - - expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); - }); - - it('ensures the license check prevents deleting actions', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - (verifyApiAccess as jest.Mock).mockImplementation(() => { - throw new Error('OMG'); - }); - - deleteActionRoute(router, licenseState); - - const [, handler] = router.delete.mock.calls[0]; - - const actionsClient = actionsClientMock.create(); - actionsClient.delete.mockResolvedValueOnce({}); - - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { - id: '1', - } - ); - - await expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); - - expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); - }); - - it('should track every call', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - const actionsClient = actionsClientMock.create(); - - deleteActionRoute(router, licenseState, mockUsageCounter); - const [, handler] = router.delete.mock.calls[0]; - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { - params: { - id: '1', - }, - } - ); - await handler(context, req, res); - expect(trackLegacyRouteUsage).toHaveBeenCalledWith('delete', mockUsageCounter); - }); -}); diff --git a/x-pack/plugins/actions/server/routes/legacy/delete.ts b/x-pack/plugins/actions/server/routes/legacy/delete.ts deleted file mode 100644 index c7e1e985cc6f0..0000000000000 --- a/x-pack/plugins/actions/server/routes/legacy/delete.ts +++ /dev/null @@ -1,68 +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 { schema } from '@kbn/config-schema'; -import { UsageCounter } from '@kbn/usage-collection-plugin/server'; -import { IRouter } from '@kbn/core/server'; -import { ILicenseState, verifyApiAccess, isErrorThatHandlesItsOwnResponse } from '../../lib'; -import { BASE_ACTION_API_PATH } from '../../../common'; -import { ActionsRequestHandlerContext } from '../../types'; -import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; - -const paramSchema = schema.object({ - id: schema.string({ - meta: { description: 'An identifier for the connector.' }, - }), -}); - -export const deleteActionRoute = ( - router: IRouter, - licenseState: ILicenseState, - usageCounter?: UsageCounter -) => { - router.delete( - { - path: `${BASE_ACTION_API_PATH}/action/{id}`, - options: { - access: 'public', - summary: `Delete a connector`, - description: 'WARNING: When you delete a connector, it cannot be recovered.', - tags: ['oas-tag:connectors'], - // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} - deprecated: true, - }, - validate: { - request: { - params: paramSchema, - }, - response: { - 204: { - description: 'Indicates a successful call.', - }, - }, - }, - }, - router.handleLegacyErrors(async function (context, req, res) { - verifyApiAccess(licenseState); - if (!context.actions) { - return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); - } - const actionsClient = (await context.actions).getActionsClient(); - const { id } = req.params; - trackLegacyRouteUsage('delete', usageCounter); - try { - await actionsClient.delete({ id }); - return res.noContent(); - } catch (e) { - if (isErrorThatHandlesItsOwnResponse(e)) { - return e.sendResponse(res); - } - throw e; - } - }) - ); -}; diff --git a/x-pack/plugins/actions/server/routes/legacy/execute.test.ts b/x-pack/plugins/actions/server/routes/legacy/execute.test.ts deleted file mode 100644 index c989731407650..0000000000000 --- a/x-pack/plugins/actions/server/routes/legacy/execute.test.ts +++ /dev/null @@ -1,214 +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 { executeActionRoute } from './execute'; -import { httpServiceMock } from '@kbn/core/server/mocks'; -import { licenseStateMock } from '../../lib/license_state.mock'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { verifyApiAccess, ActionTypeDisabledError, asHttpRequestExecutionSource } from '../../lib'; -import { actionsClientMock } from '../../actions_client/actions_client.mock'; -import { ActionTypeExecutorResult } from '../../types'; -import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; -import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; - -jest.mock('../../lib/verify_api_access', () => ({ - verifyApiAccess: jest.fn(), -})); - -jest.mock('../../lib/track_legacy_route_usage', () => ({ - trackLegacyRouteUsage: jest.fn(), -})); - -const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); -const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); - -beforeEach(() => { - jest.resetAllMocks(); -}); - -describe('executeActionRoute', () => { - it('executes an action with proper parameters', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - const actionsClient = actionsClientMock.create(); - actionsClient.execute.mockResolvedValueOnce({ status: 'ok', actionId: '1' }); - - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { - body: { - params: { - someData: 'data', - }, - }, - params: { - id: '1', - }, - }, - ['ok'] - ); - - const executeResult = { - actionId: '1', - status: 'ok', - }; - - executeActionRoute(router, licenseState); - - const [config, handler] = router.post.mock.calls[0]; - - expect(config.path).toMatchInlineSnapshot(`"/api/actions/action/{id}/_execute"`); - - expect(await handler(context, req, res)).toEqual({ body: executeResult }); - - expect(actionsClient.execute).toHaveBeenCalledWith({ - actionId: '1', - params: { - someData: 'data', - }, - source: asHttpRequestExecutionSource(req), - relatedSavedObjects: [], - }); - - expect(res.ok).toHaveBeenCalled(); - }); - - it('returns a "204 NO CONTENT" when the executor returns a nullish value', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - const actionsClient = actionsClientMock.create(); - actionsClient.execute.mockResolvedValueOnce(null as unknown as ActionTypeExecutorResult); - - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { - body: { - params: {}, - }, - params: { - id: '1', - }, - }, - ['noContent'] - ); - - executeActionRoute(router, licenseState); - - const [, handler] = router.post.mock.calls[0]; - - expect(await handler(context, req, res)).toEqual(undefined); - - expect(actionsClient.execute).toHaveBeenCalledWith({ - actionId: '1', - params: {}, - source: asHttpRequestExecutionSource(req), - relatedSavedObjects: [], - }); - - expect(res.ok).not.toHaveBeenCalled(); - expect(res.noContent).toHaveBeenCalled(); - }); - - it('ensures the license allows action execution', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - const actionsClient = actionsClientMock.create(); - actionsClient.execute.mockResolvedValue({ - actionId: '1', - status: 'ok', - }); - - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { - body: {}, - params: {}, - }, - ['ok'] - ); - - executeActionRoute(router, licenseState); - - const [, handler] = router.post.mock.calls[0]; - - await handler(context, req, res); - - expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); - }); - - it('ensures the license check prevents action execution', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - const actionsClient = actionsClientMock.create(); - actionsClient.execute.mockResolvedValue({ - actionId: '1', - status: 'ok', - }); - - (verifyApiAccess as jest.Mock).mockImplementation(() => { - throw new Error('OMG'); - }); - - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { - body: {}, - params: {}, - }, - ['ok'] - ); - - executeActionRoute(router, licenseState); - - const [, handler] = router.post.mock.calls[0]; - - await expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); - - expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); - }); - - it('ensures the action type gets validated for the license', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - const actionsClient = actionsClientMock.create(); - actionsClient.execute.mockRejectedValue(new ActionTypeDisabledError('Fail', 'license_invalid')); - - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { - body: {}, - params: {}, - }, - ['ok', 'forbidden'] - ); - - executeActionRoute(router, licenseState); - - const [, handler] = router.post.mock.calls[0]; - - await handler(context, req, res); - - expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } }); - }); - - it('should track every call', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - const actionsClient = actionsClientMock.create(); - - executeActionRoute(router, licenseState, mockUsageCounter); - const [, handler] = router.post.mock.calls[0]; - const [context, req, res] = mockHandlerArguments({ actionsClient }, { body: {}, params: {} }); - await handler(context, req, res); - expect(trackLegacyRouteUsage).toHaveBeenCalledWith('execute', mockUsageCounter); - }); -}); diff --git a/x-pack/plugins/actions/server/routes/legacy/execute.ts b/x-pack/plugins/actions/server/routes/legacy/execute.ts deleted file mode 100644 index 71b04262075d4..0000000000000 --- a/x-pack/plugins/actions/server/routes/legacy/execute.ts +++ /dev/null @@ -1,88 +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 { schema } from '@kbn/config-schema'; -import { UsageCounter } from '@kbn/usage-collection-plugin/server'; -import { IRouter } from '@kbn/core/server'; -import { ILicenseState, verifyApiAccess, isErrorThatHandlesItsOwnResponse } from '../../lib'; - -import { ActionTypeExecutorResult, ActionsRequestHandlerContext } from '../../types'; -import { BASE_ACTION_API_PATH } from '../../../common'; -import { asHttpRequestExecutionSource } from '../../lib/action_execution_source'; -import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; -import { connectorResponseSchemaV1 } from '../../../common/routes/connector/response'; - -const paramSchema = schema.object({ - id: schema.string({ - meta: { description: 'An identifier for the connector.' }, - }), -}); - -const bodySchema = schema.object({ - params: schema.recordOf(schema.string(), schema.any()), -}); - -export const executeActionRoute = ( - router: IRouter, - licenseState: ILicenseState, - usageCounter?: UsageCounter -) => { - router.post( - { - path: `${BASE_ACTION_API_PATH}/action/{id}/_execute`, - options: { - access: 'public', - summary: `Run a connector`, - // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} - deprecated: true, - tags: ['oas-tag:connectors'], - }, - validate: { - request: { - body: bodySchema, - params: paramSchema, - }, - response: { - 200: { - description: 'Indicates a successful call.', - body: () => connectorResponseSchemaV1, - }, - }, - }, - }, - router.handleLegacyErrors(async function (context, req, res) { - verifyApiAccess(licenseState); - - if (!context.actions) { - return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); - } - - const actionsClient = (await context.actions).getActionsClient(); - const { params } = req.body; - const { id } = req.params; - trackLegacyRouteUsage('execute', usageCounter); - try { - const body: ActionTypeExecutorResult = await actionsClient.execute({ - params, - actionId: id, - source: asHttpRequestExecutionSource(req), - relatedSavedObjects: [], - }); - return body - ? res.ok({ - body, - }) - : res.noContent(); - } catch (e) { - if (isErrorThatHandlesItsOwnResponse(e)) { - return e.sendResponse(res); - } - throw e; - } - }) - ); -}; diff --git a/x-pack/plugins/actions/server/routes/legacy/get.test.ts b/x-pack/plugins/actions/server/routes/legacy/get.test.ts deleted file mode 100644 index 732c964fb8284..0000000000000 --- a/x-pack/plugins/actions/server/routes/legacy/get.test.ts +++ /dev/null @@ -1,165 +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 { getActionRoute } from './get'; -import { httpServiceMock } from '@kbn/core/server/mocks'; -import { licenseStateMock } from '../../lib/license_state.mock'; -import { verifyApiAccess } from '../../lib'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { actionsClientMock } from '../../actions_client/actions_client.mock'; -import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; -import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; - -jest.mock('../../lib/verify_api_access', () => ({ - verifyApiAccess: jest.fn(), -})); - -jest.mock('../../lib/track_legacy_route_usage', () => ({ - trackLegacyRouteUsage: jest.fn(), -})); - -const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); -const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); - -beforeEach(() => { - jest.resetAllMocks(); -}); - -describe('getActionRoute', () => { - it('gets an action with proper parameters', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - getActionRoute(router, licenseState); - - const [config, handler] = router.get.mock.calls[0]; - - expect(config.path).toMatchInlineSnapshot(`"/api/actions/action/{id}"`); - - const getResult = { - id: '1', - actionTypeId: '2', - name: 'action name', - config: {}, - isPreconfigured: false, - isDeprecated: false, - isSystemAction: false, - }; - - const actionsClient = actionsClientMock.create(); - actionsClient.get.mockResolvedValueOnce(getResult); - - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { - params: { id: '1' }, - }, - ['ok'] - ); - - expect(await handler(context, req, res)).toMatchInlineSnapshot(` - Object { - "body": Object { - "actionTypeId": "2", - "config": Object {}, - "id": "1", - "isDeprecated": false, - "isPreconfigured": false, - "isSystemAction": false, - "name": "action name", - }, - } - `); - - expect(actionsClient.get).toHaveBeenCalledTimes(1); - expect(actionsClient.get.mock.calls[0][0].id).toEqual('1'); - - expect(res.ok).toHaveBeenCalledWith({ - body: getResult, - }); - }); - - it('ensures the license allows getting actions', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - getActionRoute(router, licenseState); - - const [, handler] = router.get.mock.calls[0]; - - const actionsClient = actionsClientMock.create(); - actionsClient.get.mockResolvedValueOnce({ - id: '1', - actionTypeId: '2', - name: 'action name', - config: {}, - isPreconfigured: false, - isDeprecated: false, - isSystemAction: false, - }); - - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { - params: { id: '1' }, - }, - ['ok'] - ); - - await handler(context, req, res); - - expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); - }); - - it('ensures the license check prevents getting actions', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - (verifyApiAccess as jest.Mock).mockImplementation(() => { - throw new Error('OMG'); - }); - - getActionRoute(router, licenseState); - - const [, handler] = router.get.mock.calls[0]; - - const actionsClient = actionsClientMock.create(); - actionsClient.get.mockResolvedValueOnce({ - id: '1', - actionTypeId: '2', - name: 'action name', - config: {}, - isPreconfigured: false, - isDeprecated: false, - isSystemAction: false, - }); - - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { - params: { id: '1' }, - }, - ['ok'] - ); - - await expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); - - expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); - }); - - it('should track every call', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - const actionsClient = actionsClientMock.create(); - - getActionRoute(router, licenseState, mockUsageCounter); - const [, handler] = router.get.mock.calls[0]; - const [context, req, res] = mockHandlerArguments({ actionsClient }, { params: { id: '1' } }); - await handler(context, req, res); - expect(trackLegacyRouteUsage).toHaveBeenCalledWith('get', mockUsageCounter); - }); -}); diff --git a/x-pack/plugins/actions/server/routes/legacy/get.ts b/x-pack/plugins/actions/server/routes/legacy/get.ts deleted file mode 100644 index 571849ccaf478..0000000000000 --- a/x-pack/plugins/actions/server/routes/legacy/get.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { schema } from '@kbn/config-schema'; -import { UsageCounter } from '@kbn/usage-collection-plugin/server'; -import { IRouter } from '@kbn/core/server'; -import { ILicenseState, verifyApiAccess } from '../../lib'; -import { BASE_ACTION_API_PATH } from '../../../common'; -import { ActionsRequestHandlerContext } from '../../types'; -import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; -import { connectorResponseSchemaV1 } from '../../../common/routes/connector/response'; - -const paramSchema = schema.object({ - id: schema.string({ - meta: { description: 'An identifier for the connector.' }, - }), -}); - -export const getActionRoute = ( - router: IRouter, - licenseState: ILicenseState, - usageCounter?: UsageCounter -) => { - router.get( - { - path: `${BASE_ACTION_API_PATH}/action/{id}`, - options: { - access: 'public', - summary: `Get connector information`, - // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} - deprecated: true, - tags: ['oas-tag:connectors'], - }, - validate: { - request: { - params: paramSchema, - }, - response: { - 200: { - description: 'Indicates a successful call.', - body: () => connectorResponseSchemaV1, - }, - }, - }, - }, - router.handleLegacyErrors(async function (context, req, res) { - verifyApiAccess(licenseState); - if (!context.actions) { - return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); - } - const actionsClient = (await context.actions).getActionsClient(); - const { id } = req.params; - trackLegacyRouteUsage('get', usageCounter); - return res.ok({ - body: await actionsClient.get({ id }), - }); - }) - ); -}; diff --git a/x-pack/plugins/actions/server/routes/legacy/get_all.test.ts b/x-pack/plugins/actions/server/routes/legacy/get_all.test.ts deleted file mode 100644 index e8657e56259e1..0000000000000 --- a/x-pack/plugins/actions/server/routes/legacy/get_all.test.ts +++ /dev/null @@ -1,116 +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 { getAllActionRoute } from './get_all'; -import { httpServiceMock } from '@kbn/core/server/mocks'; -import { licenseStateMock } from '../../lib/license_state.mock'; -import { verifyApiAccess } from '../../lib'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { actionsClientMock } from '../../actions_client/actions_client.mock'; -import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; -import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; - -jest.mock('../../lib/verify_api_access', () => ({ - verifyApiAccess: jest.fn(), -})); - -jest.mock('../../lib/track_legacy_route_usage', () => ({ - trackLegacyRouteUsage: jest.fn(), -})); - -const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); -const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); - -beforeEach(() => { - jest.resetAllMocks(); -}); - -describe('getAllActionRoute', () => { - it('get all actions with proper parameters', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - getAllActionRoute(router, licenseState); - - const [config, handler] = router.get.mock.calls[0]; - - expect(config.path).toMatchInlineSnapshot(`"/api/actions"`); - - const actionsClient = actionsClientMock.create(); - actionsClient.getAll.mockResolvedValueOnce([]); - - const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); - - expect(await handler(context, req, res)).toMatchInlineSnapshot(` - Object { - "body": Array [], - } - `); - - expect(actionsClient.getAll).toHaveBeenCalledTimes(1); - - expect(res.ok).toHaveBeenCalledWith({ - body: [], - }); - }); - - it('ensures the license allows getting all actions', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - getAllActionRoute(router, licenseState); - - const [config, handler] = router.get.mock.calls[0]; - - expect(config.path).toMatchInlineSnapshot(`"/api/actions"`); - - const actionsClient = actionsClientMock.create(); - actionsClient.getAll.mockResolvedValueOnce([]); - - const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); - - await handler(context, req, res); - - expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); - }); - - it('ensures the license check prevents getting all actions', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - (verifyApiAccess as jest.Mock).mockImplementation(() => { - throw new Error('OMG'); - }); - - getAllActionRoute(router, licenseState); - - const [config, handler] = router.get.mock.calls[0]; - - expect(config.path).toMatchInlineSnapshot(`"/api/actions"`); - - const actionsClient = actionsClientMock.create(); - actionsClient.getAll.mockResolvedValueOnce([]); - - const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); - - await expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); - - expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); - }); - - it('should track every call', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - const actionsClient = actionsClientMock.create(); - - getAllActionRoute(router, licenseState, mockUsageCounter); - const [, handler] = router.get.mock.calls[0]; - const [context, req, res] = mockHandlerArguments({ actionsClient }, {}); - await handler(context, req, res); - expect(trackLegacyRouteUsage).toHaveBeenCalledWith('getAll', mockUsageCounter); - }); -}); diff --git a/x-pack/plugins/actions/server/routes/legacy/get_all.ts b/x-pack/plugins/actions/server/routes/legacy/get_all.ts deleted file mode 100644 index f0a17acb96691..0000000000000 --- a/x-pack/plugins/actions/server/routes/legacy/get_all.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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { IRouter } from '@kbn/core/server'; -import { UsageCounter } from '@kbn/usage-collection-plugin/server'; -import { ILicenseState, verifyApiAccess } from '../../lib'; -import { BASE_ACTION_API_PATH } from '../../../common'; -import { ActionsRequestHandlerContext } from '../../types'; -import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; - -export const getAllActionRoute = ( - router: IRouter, - licenseState: ILicenseState, - usageCounter?: UsageCounter -) => { - router.get( - { - path: `${BASE_ACTION_API_PATH}`, - options: { - access: 'public', - summary: `Get all connectors`, - // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} - deprecated: true, - tags: ['oas-tag:connectors'], - }, - validate: {}, - }, - router.handleLegacyErrors(async function (context, req, res) { - verifyApiAccess(licenseState); - if (!context.actions) { - return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); - } - const actionsClient = (await context.actions).getActionsClient(); - const result = await actionsClient.getAll(); - trackLegacyRouteUsage('getAll', usageCounter); - return res.ok({ - body: result, - }); - }) - ); -}; diff --git a/x-pack/plugins/actions/server/routes/legacy/index.ts b/x-pack/plugins/actions/server/routes/legacy/index.ts deleted file mode 100644 index 37ed5efbd99b9..0000000000000 --- a/x-pack/plugins/actions/server/routes/legacy/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { IRouter } from '@kbn/core/server'; -import { UsageCounter } from '@kbn/usage-collection-plugin/server'; -import { ILicenseState } from '../../lib'; -import { ActionsRequestHandlerContext } from '../../types'; -import { createActionRoute } from './create'; -import { deleteActionRoute } from './delete'; -import { getAllActionRoute } from './get_all'; -import { getActionRoute } from './get'; -import { updateActionRoute } from './update'; -import { listActionTypesRoute } from './list_action_types'; -import { executeActionRoute } from './execute'; - -export function defineLegacyRoutes( - router: IRouter, - licenseState: ILicenseState, - usageCounter?: UsageCounter -) { - createActionRoute(router, licenseState, usageCounter); - deleteActionRoute(router, licenseState, usageCounter); - getActionRoute(router, licenseState, usageCounter); - getAllActionRoute(router, licenseState, usageCounter); - updateActionRoute(router, licenseState, usageCounter); - listActionTypesRoute(router, licenseState, usageCounter); - executeActionRoute(router, licenseState, usageCounter); -} diff --git a/x-pack/plugins/actions/server/routes/legacy/list_action_types.test.ts b/x-pack/plugins/actions/server/routes/legacy/list_action_types.test.ts deleted file mode 100644 index ec57c4b9a99a9..0000000000000 --- a/x-pack/plugins/actions/server/routes/legacy/list_action_types.test.ts +++ /dev/null @@ -1,178 +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 { listActionTypesRoute } from './list_action_types'; -import { httpServiceMock } from '@kbn/core/server/mocks'; -import { licenseStateMock } from '../../lib/license_state.mock'; -import { verifyApiAccess } from '../../lib'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { LicenseType } from '@kbn/licensing-plugin/server'; -import { actionsClientMock } from '../../mocks'; -import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; -import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; - -jest.mock('../../lib/verify_api_access', () => ({ - verifyApiAccess: jest.fn(), -})); - -jest.mock('../../lib/track_legacy_route_usage', () => ({ - trackLegacyRouteUsage: jest.fn(), -})); - -const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); -const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); - -beforeEach(() => { - jest.resetAllMocks(); -}); - -describe('listActionTypesRoute', () => { - it('lists action types with proper parameters', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - listActionTypesRoute(router, licenseState); - - const [config, handler] = router.get.mock.calls[0]; - - expect(config.path).toMatchInlineSnapshot(`"/api/actions/list_action_types"`); - - const listTypes = [ - { - id: '1', - name: 'name', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - minimumLicenseRequired: 'gold' as LicenseType, - supportedFeatureIds: ['alerting'], - isSystemActionType: false, - }, - ]; - - const actionsClient = actionsClientMock.create(); - actionsClient.listTypes.mockResolvedValueOnce(listTypes); - const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); - - expect(await handler(context, req, res)).toMatchInlineSnapshot(` - Object { - "body": Array [ - Object { - "enabled": true, - "enabledInConfig": true, - "enabledInLicense": true, - "id": "1", - "isSystemActionType": false, - "minimumLicenseRequired": "gold", - "name": "name", - "supportedFeatureIds": Array [ - "alerting", - ], - }, - ], - } - `); - - expect(res.ok).toHaveBeenCalledWith({ - body: listTypes, - }); - }); - - it('ensures the license allows listing action types', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - listActionTypesRoute(router, licenseState); - - const [config, handler] = router.get.mock.calls[0]; - - expect(config.path).toMatchInlineSnapshot(`"/api/actions/list_action_types"`); - - const listTypes = [ - { - id: '1', - name: 'name', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - minimumLicenseRequired: 'gold' as LicenseType, - supportedFeatureIds: ['alerting'], - isSystemActionType: false, - }, - ]; - - const actionsClient = actionsClientMock.create(); - actionsClient.listTypes.mockResolvedValueOnce(listTypes); - - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { - params: { id: '1' }, - }, - ['ok'] - ); - - await handler(context, req, res); - - expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); - }); - - it('ensures the license check prevents listing action types', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - (verifyApiAccess as jest.Mock).mockImplementation(() => { - throw new Error('OMG'); - }); - - listActionTypesRoute(router, licenseState); - - const [config, handler] = router.get.mock.calls[0]; - - expect(config.path).toMatchInlineSnapshot(`"/api/actions/list_action_types"`); - - const listTypes = [ - { - id: '1', - name: 'name', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - minimumLicenseRequired: 'gold' as LicenseType, - supportedFeatureIds: ['alerting'], - isSystemActionType: false, - }, - ]; - - const actionsClient = actionsClientMock.create(); - actionsClient.listTypes.mockResolvedValueOnce(listTypes); - - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { - params: { id: '1' }, - }, - ['ok'] - ); - - await expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); - - expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); - }); - - it('should track every call', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - const actionsClient = actionsClientMock.create(); - - listActionTypesRoute(router, licenseState, mockUsageCounter); - const [, handler] = router.get.mock.calls[0]; - const [context, req, res] = mockHandlerArguments({ actionsClient }, {}); - await handler(context, req, res); - expect(trackLegacyRouteUsage).toHaveBeenCalledWith('listActionTypes', mockUsageCounter); - }); -}); diff --git a/x-pack/plugins/actions/server/routes/legacy/list_action_types.ts b/x-pack/plugins/actions/server/routes/legacy/list_action_types.ts deleted file mode 100644 index cc3e9c23f240d..0000000000000 --- a/x-pack/plugins/actions/server/routes/legacy/list_action_types.ts +++ /dev/null @@ -1,48 +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 { IRouter } from '@kbn/core/server'; -import { UsageCounter } from '@kbn/usage-collection-plugin/server'; -import { ILicenseState, verifyApiAccess } from '../../lib'; -import { BASE_ACTION_API_PATH } from '../../../common'; -import { ActionsRequestHandlerContext } from '../../types'; -import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; - -/** - * Return all available action types - * expect system action types - */ -export const listActionTypesRoute = ( - router: IRouter, - licenseState: ILicenseState, - usageCounter?: UsageCounter -) => { - router.get( - { - path: `${BASE_ACTION_API_PATH}/list_action_types`, - options: { - access: 'public', - summary: `Get connector types`, - // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} - deprecated: true, - tags: ['oas-tag:connectors'], - }, - validate: {}, - }, - router.handleLegacyErrors(async function (context, req, res) { - verifyApiAccess(licenseState); - if (!context.actions) { - return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); - } - const actionsClient = (await context.actions).getActionsClient(); - trackLegacyRouteUsage('listActionTypes', usageCounter); - return res.ok({ - body: await actionsClient.listTypes(), - }); - }) - ); -}; diff --git a/x-pack/plugins/actions/server/routes/legacy/update.test.ts b/x-pack/plugins/actions/server/routes/legacy/update.test.ts deleted file mode 100644 index 493d1c873690e..0000000000000 --- a/x-pack/plugins/actions/server/routes/legacy/update.test.ts +++ /dev/null @@ -1,215 +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 { updateActionRoute } from './update'; -import { httpServiceMock } from '@kbn/core/server/mocks'; -import { licenseStateMock } from '../../lib/license_state.mock'; -import { verifyApiAccess, ActionTypeDisabledError } from '../../lib'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { actionsClientMock } from '../../actions_client/actions_client.mock'; -import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; -import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; - -jest.mock('../../lib/verify_api_access', () => ({ - verifyApiAccess: jest.fn(), -})); - -jest.mock('../../lib/track_legacy_route_usage', () => ({ - trackLegacyRouteUsage: jest.fn(), -})); - -const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); -const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); - -beforeEach(() => { - jest.resetAllMocks(); -}); - -describe('updateActionRoute', () => { - it('updates an action with proper parameters', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - updateActionRoute(router, licenseState); - - const [config, handler] = router.put.mock.calls[0]; - - expect(config.path).toMatchInlineSnapshot(`"/api/actions/action/{id}"`); - - const updateResult = { - id: '1', - actionTypeId: 'my-action-type-id', - name: 'My name', - config: { foo: true }, - isPreconfigured: false, - isDeprecated: false, - isSystemAction: false, - }; - - const actionsClient = actionsClientMock.create(); - actionsClient.update.mockResolvedValueOnce(updateResult); - - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { - params: { - id: '1', - }, - body: { - name: 'My name', - config: { foo: true }, - secrets: { key: 'i8oh34yf9783y39' }, - }, - }, - ['ok'] - ); - - expect(await handler(context, req, res)).toEqual({ body: updateResult }); - - expect(actionsClient.update).toHaveBeenCalledTimes(1); - expect(actionsClient.update.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "action": Object { - "config": Object { - "foo": true, - }, - "name": "My name", - "secrets": Object { - "key": "i8oh34yf9783y39", - }, - }, - "id": "1", - }, - ] - `); - - expect(res.ok).toHaveBeenCalled(); - }); - - it('ensures the license allows deleting actions', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - updateActionRoute(router, licenseState); - - const [, handler] = router.put.mock.calls[0]; - - const updateResult = { - id: '1', - actionTypeId: 'my-action-type-id', - name: 'My name', - config: { foo: true }, - isPreconfigured: false, - isDeprecated: false, - isSystemAction: false, - }; - - const actionsClient = actionsClientMock.create(); - actionsClient.update.mockResolvedValueOnce(updateResult); - - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { - params: { - id: '1', - }, - body: { - name: 'My name', - config: { foo: true }, - secrets: { key: 'i8oh34yf9783y39' }, - }, - }, - ['ok'] - ); - - await handler(context, req, res); - - expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); - }); - - it('ensures the license check prevents deleting actions', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - (verifyApiAccess as jest.Mock).mockImplementation(() => { - throw new Error('OMG'); - }); - - updateActionRoute(router, licenseState); - - const [, handler] = router.put.mock.calls[0]; - - const updateResult = { - id: '1', - actionTypeId: 'my-action-type-id', - name: 'My name', - config: { foo: true }, - isPreconfigured: false, - isDeprecated: false, - isSystemAction: false, - }; - - const actionsClient = actionsClientMock.create(); - actionsClient.update.mockResolvedValueOnce(updateResult); - - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { - params: { - id: '1', - }, - body: { - name: 'My name', - config: { foo: true }, - secrets: { key: 'i8oh34yf9783y39' }, - }, - }, - ['ok'] - ); - - await expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); - - expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); - }); - - it('ensures the action type gets validated for the license', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - updateActionRoute(router, licenseState); - - const [, handler] = router.put.mock.calls[0]; - - const actionsClient = actionsClientMock.create(); - actionsClient.update.mockRejectedValue(new ActionTypeDisabledError('Fail', 'license_invalid')); - - const [context, req, res] = mockHandlerArguments({ actionsClient }, { params: {}, body: {} }, [ - 'ok', - 'forbidden', - ]); - - await handler(context, req, res); - - expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } }); - }); - - it('should track every call', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - const actionsClient = actionsClientMock.create(); - - updateActionRoute(router, licenseState, mockUsageCounter); - const [, handler] = router.put.mock.calls[0]; - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { params: { id: '1' }, body: {} } - ); - await handler(context, req, res); - expect(trackLegacyRouteUsage).toHaveBeenCalledWith('update', mockUsageCounter); - }); -}); diff --git a/x-pack/plugins/actions/server/routes/legacy/update.ts b/x-pack/plugins/actions/server/routes/legacy/update.ts deleted file mode 100644 index 0bf1ec7ece55d..0000000000000 --- a/x-pack/plugins/actions/server/routes/legacy/update.ts +++ /dev/null @@ -1,82 +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 { schema } from '@kbn/config-schema'; -import { UsageCounter } from '@kbn/usage-collection-plugin/server'; -import { IRouter } from '@kbn/core/server'; -import { ILicenseState, verifyApiAccess, isErrorThatHandlesItsOwnResponse } from '../../lib'; -import { BASE_ACTION_API_PATH } from '../../../common'; -import { ActionsRequestHandlerContext } from '../../types'; -import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; -import { connectorResponseSchemaV1 } from '../../../common/routes/connector/response'; - -const paramSchema = schema.object({ - id: schema.string({ - meta: { description: 'An identifier for the connector.' }, - }), -}); - -const bodySchema = schema.object({ - name: schema.string(), - config: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), - secrets: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), -}); - -export const updateActionRoute = ( - router: IRouter, - licenseState: ILicenseState, - usageCounter?: UsageCounter -) => { - router.put( - { - path: `${BASE_ACTION_API_PATH}/action/{id}`, - options: { - access: 'public', - summary: `Update a connector`, - // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} - deprecated: true, - tags: ['oas-tag:connectors'], - }, - validate: { - request: { - body: bodySchema, - params: paramSchema, - }, - response: { - 200: { - description: 'Indicates a successful call.', - body: () => connectorResponseSchemaV1, - }, - }, - }, - }, - router.handleLegacyErrors(async function (context, req, res) { - verifyApiAccess(licenseState); - if (!context.actions) { - return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); - } - const actionsClient = (await context.actions).getActionsClient(); - const { id } = req.params; - const { name, config, secrets } = req.body; - trackLegacyRouteUsage('update', usageCounter); - - try { - return res.ok({ - body: await actionsClient.update({ - id, - action: { name, config, secrets }, - }), - }); - } catch (e) { - if (isErrorThatHandlesItsOwnResponse(e)) { - return e.sendResponse(res); - } - throw e; - } - }) - ); -}; diff --git a/x-pack/plugins/actions/server/routes/verify_access_and_context.test.ts b/x-pack/plugins/actions/server/routes/verify_access_and_context.test.ts index e079634fbfeff..7c1088e8c1d9e 100644 --- a/x-pack/plugins/actions/server/routes/verify_access_and_context.test.ts +++ b/x-pack/plugins/actions/server/routes/verify_access_and_context.test.ts @@ -7,7 +7,7 @@ import { licenseStateMock } from '../lib/license_state.mock'; import { verifyApiAccess, ActionTypeDisabledError } from '../lib'; -import { mockHandlerArguments } from './legacy/_mock_handler_arguments'; +import { mockHandlerArguments } from './_mock_handler_arguments'; import { actionsClientMock } from '../actions_client/actions_client.mock'; import { verifyAccessAndContext } from './verify_access_and_context'; diff --git a/x-pack/plugins/actions/server/usage/connector_usage_reporting_task.test.ts b/x-pack/plugins/actions/server/usage/connector_usage_reporting_task.test.ts new file mode 100644 index 0000000000000..77dec7f15e156 --- /dev/null +++ b/x-pack/plugins/actions/server/usage/connector_usage_reporting_task.test.ts @@ -0,0 +1,394 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import fs from 'fs'; +import axios from 'axios'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { coreMock } from '@kbn/core/server/mocks'; +import { + TaskManagerSetupContract, + TaskManagerStartContract, + TaskStatus, +} from '@kbn/task-manager-plugin/server'; +import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; +import { + CONNECTOR_USAGE_REPORTING_SOURCE_ID, + CONNECTOR_USAGE_REPORTING_TASK_ID, + CONNECTOR_USAGE_REPORTING_TASK_SCHEDULE, + CONNECTOR_USAGE_REPORTING_TASK_TYPE, + ConnectorUsageReportingTask, +} from './connector_usage_reporting_task'; +import type { CoreSetup, ElasticsearchClient } from '@kbn/core/server'; +import { ActionsPluginsStart } from '../plugin'; +import { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; + +jest.mock('axios'); +const mockedAxiosPost = jest.spyOn(axios, 'post'); + +const nowStr = '2024-01-01T12:00:00.000Z'; +const nowDate = new Date(nowStr); + +jest.useFakeTimers(); +jest.setSystemTime(nowDate.getTime()); +const readFileSpy = jest.spyOn(fs, 'readFileSync'); + +describe('ConnectorUsageReportingTask', () => { + const logger = loggingSystemMock.createLogger(); + const { createSetup } = coreMock; + const { createSetup: taskManagerSetupMock, createStart: taskManagerStartMock } = taskManagerMock; + let mockEsClient: jest.Mocked; + let mockCore: CoreSetup; + let mockTaskManagerSetup: jest.Mocked; + let mockTaskManagerStart: jest.Mocked; + + beforeEach(async () => { + mockTaskManagerSetup = taskManagerSetupMock(); + mockTaskManagerStart = taskManagerStartMock(); + mockCore = createSetup(); + mockEsClient = (await mockCore.getStartServices())[0].elasticsearch.client + .asInternalUser as jest.Mocked; + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + const createTaskRunner = async ({ + lastReportedUsageDate, + projectId, + attempts = 0, + }: { + lastReportedUsageDate: Date; + projectId?: string; + attempts?: number; + }) => { + const timestamp = new Date(new Date().setMinutes(-15)); + const task = new ConnectorUsageReportingTask({ + eventLogIndex: 'test-index', + projectId, + logger, + core: mockCore, + taskManager: mockTaskManagerSetup, + config: { + url: 'usage-api', + ca: { + path: './ca.crt', + }, + }, + }); + + await task.start(mockTaskManagerStart); + + const createTaskRunnerFunction = + mockTaskManagerSetup.registerTaskDefinitions.mock.calls[0][0][ + CONNECTOR_USAGE_REPORTING_TASK_TYPE + ].createTaskRunner; + + return createTaskRunnerFunction({ + taskInstance: { + id: CONNECTOR_USAGE_REPORTING_TASK_ID, + runAt: timestamp, + attempts: 0, + ownerId: '', + status: TaskStatus.Running, + startedAt: timestamp, + scheduledAt: timestamp, + retryAt: null, + params: {}, + state: { + lastReportedUsageDate, + attempts, + }, + taskType: CONNECTOR_USAGE_REPORTING_TASK_TYPE, + }, + }); + }; + + it('registers the task', async () => { + readFileSpy.mockImplementationOnce(() => '---CA CERTIFICATE---'); + new ConnectorUsageReportingTask({ + eventLogIndex: 'test-index', + projectId: 'test-projecr', + logger, + core: createSetup(), + taskManager: mockTaskManagerSetup, + config: { + url: 'usage-api', + ca: { + path: './ca.crt', + }, + }, + }); + + expect(mockTaskManagerSetup.registerTaskDefinitions).toBeCalledTimes(1); + expect(mockTaskManagerSetup.registerTaskDefinitions).toHaveBeenCalledWith({ + [CONNECTOR_USAGE_REPORTING_TASK_TYPE]: { + title: 'Connector usage reporting task', + timeout: '1m', + createTaskRunner: expect.any(Function), + }, + }); + }); + + it('schedules the task', async () => { + readFileSpy.mockImplementationOnce(() => '---CA CERTIFICATE---'); + const core = createSetup(); + const taskManagerStart = taskManagerStartMock(); + + const task = new ConnectorUsageReportingTask({ + eventLogIndex: 'test-index', + projectId: 'test-projecr', + logger, + core, + taskManager: mockTaskManagerSetup, + config: { + url: 'usage-api', + ca: { + path: './ca.crt', + }, + }, + }); + + await task.start(taskManagerStart); + + expect(taskManagerStart.ensureScheduled).toBeCalledTimes(1); + expect(taskManagerStart.ensureScheduled).toHaveBeenCalledWith({ + id: CONNECTOR_USAGE_REPORTING_TASK_ID, + taskType: CONNECTOR_USAGE_REPORTING_TASK_TYPE, + schedule: { + ...CONNECTOR_USAGE_REPORTING_TASK_SCHEDULE, + }, + state: {}, + params: {}, + }); + }); + + it('logs error if task manager is not ready', async () => { + readFileSpy.mockImplementationOnce(() => '---CA CERTIFICATE---'); + const core = createSetup(); + const taskManagerStart = taskManagerStartMock(); + + const task = new ConnectorUsageReportingTask({ + eventLogIndex: 'test-index', + projectId: 'test-projecr', + logger, + core, + taskManager: mockTaskManagerSetup, + config: { + url: 'usage-api', + ca: { + path: './ca.crt', + }, + }, + }); + + await task.start(); + + expect(taskManagerStart.ensureScheduled).not.toBeCalled(); + expect(logger.error).toHaveBeenCalledWith( + `Missing required task manager service during start of ${CONNECTOR_USAGE_REPORTING_TASK_TYPE}` + ); + }); + + it('logs error if scheduling task fails', async () => { + readFileSpy.mockImplementationOnce(() => '---CA CERTIFICATE---'); + const core = createSetup(); + const taskManagerStart = taskManagerStartMock(); + taskManagerStart.ensureScheduled.mockRejectedValue(new Error('test')); + + const task = new ConnectorUsageReportingTask({ + eventLogIndex: 'test-index', + projectId: 'test-projecr', + logger, + core, + taskManager: mockTaskManagerSetup, + config: { + url: 'usage-api', + ca: { + path: './ca.crt', + }, + }, + }); + + await task.start(taskManagerStart); + + expect(logger.error).toHaveBeenCalledWith( + 'Error scheduling task actions:connector_usage_reporting, received test' + ); + }); + + it('returns the existing state and logs a warning when project id is missing', async () => { + const lastReportedUsageDateStr = '2024-01-01T00:00:00.000Z'; + const lastReportedUsageDate = new Date(lastReportedUsageDateStr); + + const taskRunner = await createTaskRunner({ lastReportedUsageDate }); + + const response = await taskRunner.run(); + + expect(logger.warn).toHaveBeenCalledWith( + 'Missing required project id while running actions:connector_usage_reporting' + ); + + expect(response).toEqual({ + state: { + attempts: 0, + lastReportedUsageDate, + }, + }); + }); + + it('returns the existing state and logs an error when the CA Certificate is missing', async () => { + const lastReportedUsageDateStr = '2024-01-01T00:00:00.000Z'; + const lastReportedUsageDate = new Date(lastReportedUsageDateStr); + readFileSpy.mockImplementationOnce((func) => { + throw new Error('Mock file read error.'); + }); + + const taskRunner = await createTaskRunner({ lastReportedUsageDate, projectId: 'test-id' }); + + const response = await taskRunner.run(); + + expect(logger.error).toHaveBeenCalledTimes(2); + + expect(logger.error).toHaveBeenNthCalledWith( + 1, + `CA Certificate for the project "test-id" couldn't be loaded, Error: Mock file read error.` + ); + + expect(logger.error).toHaveBeenNthCalledWith( + 2, + 'Missing required CA Certificate while running actions:connector_usage_reporting' + ); + + expect(response).toEqual({ + state: { + attempts: 0, + lastReportedUsageDate, + }, + }); + }); + + it('runs the task', async () => { + readFileSpy.mockImplementationOnce(() => '---CA CERTIFICATE---'); + mockEsClient.search.mockResolvedValueOnce({ + aggregations: { total_usage: 215 }, + } as SearchResponse); + + mockedAxiosPost.mockResolvedValueOnce(200); + + const lastReportedUsageDateStr = '2024-01-01T00:00:00.000Z'; + const lastReportedUsageDate = new Date(lastReportedUsageDateStr); + + const taskRunner = await createTaskRunner({ lastReportedUsageDate, projectId: 'test-project' }); + + const response = await taskRunner.run(); + + const report = [ + { + creation_timestamp: nowStr, + id: 'connector-request-body-bytes-test-project-2024-01-01T12:00:00.000Z', + source: { + id: CONNECTOR_USAGE_REPORTING_SOURCE_ID, + instance_group_id: 'test-project', + }, + usage: { + period_seconds: 43200, + quantity: 0, + type: 'connector_request_body_bytes', + }, + usage_timestamp: nowStr, + }, + ]; + + expect(mockedAxiosPost).toHaveBeenCalledWith('usage-api', report, { + headers: { 'Content-Type': 'application/json' }, + timeout: 30000, + httpsAgent: expect.any(Object), + }); + + expect(response).toEqual({ + state: { + attempts: 0, + lastReportedUsageDate: expect.any(Date), + }, + }); + }); + + it('re-runs the task when search for records fails', async () => { + readFileSpy.mockImplementationOnce(() => '---CA CERTIFICATE---'); + mockEsClient.search.mockRejectedValue(new Error('500')); + + mockedAxiosPost.mockResolvedValueOnce(200); + + const lastReportedUsageDate = new Date('2024-01-01T00:00:00.000Z'); + + const taskRunner = await createTaskRunner({ lastReportedUsageDate, projectId: 'test-project' }); + + const response = await taskRunner.run(); + + expect(response).toEqual({ + state: { + lastReportedUsageDate, + attempts: 0, + }, + runAt: nowDate, + }); + }); + + it('re-runs the task when it fails to push the usage record', async () => { + readFileSpy.mockImplementationOnce(() => '---CA CERTIFICATE---'); + mockEsClient.search.mockResolvedValueOnce({ + aggregations: { total_usage: 215 }, + } as SearchResponse); + + mockedAxiosPost.mockRejectedValueOnce(500); + + const lastReportedUsageDate = new Date('2024-01-01T00:00:00.000Z'); + + const taskRunner = await createTaskRunner({ lastReportedUsageDate, projectId: 'test-project' }); + + const response = await taskRunner.run(); + + expect(response).toEqual({ + state: { + lastReportedUsageDate, + attempts: 1, + }, + runAt: new Date(nowDate.getTime() + 60000), // After a min + }); + }); + + it('stops retrying after 5 attempts', async () => { + readFileSpy.mockImplementationOnce(() => '---CA CERTIFICATE---'); + mockEsClient.search.mockResolvedValueOnce({ + aggregations: { total_usage: 215 }, + } as SearchResponse); + + mockedAxiosPost.mockRejectedValueOnce(new Error('test-error')); + + const lastReportedUsageDate = new Date('2024-01-01T00:00:00.000Z'); + + const taskRunner = await createTaskRunner({ + lastReportedUsageDate, + projectId: 'test-project', + attempts: 4, + }); + + const response = await taskRunner.run(); + + expect(response).toEqual({ + state: { + lastReportedUsageDate, + attempts: 0, + }, + }); + + expect(logger.error).toHaveBeenCalledWith( + 'Usage data could not be pushed to usage-api. Stopped retrying after 5 attempts. Error:test-error' + ); + }); +}); diff --git a/x-pack/plugins/actions/server/usage/connector_usage_reporting_task.ts b/x-pack/plugins/actions/server/usage/connector_usage_reporting_task.ts new file mode 100644 index 0000000000000..ce44718749006 --- /dev/null +++ b/x-pack/plugins/actions/server/usage/connector_usage_reporting_task.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 fs from 'fs'; +import { Logger, CoreSetup, type ElasticsearchClient } from '@kbn/core/server'; +import { + IntervalSchedule, + type ConcreteTaskInstance, + TaskManagerStartContract, + TaskManagerSetupContract, +} from '@kbn/task-manager-plugin/server'; +import { AggregationsSumAggregate } from '@elastic/elasticsearch/lib/api/types'; +import axios from 'axios'; +import https from 'https'; +import { ActionsConfig } from '../config'; +import { ConnectorUsageReport } from './types'; +import { ActionsPluginsStart } from '../plugin'; + +export const CONNECTOR_USAGE_REPORTING_TASK_SCHEDULE: IntervalSchedule = { interval: '1h' }; +export const CONNECTOR_USAGE_REPORTING_TASK_ID = 'connector_usage_reporting'; +export const CONNECTOR_USAGE_REPORTING_TASK_TYPE = `actions:${CONNECTOR_USAGE_REPORTING_TASK_ID}`; +export const CONNECTOR_USAGE_REPORTING_TASK_TIMEOUT = 30000; +export const CONNECTOR_USAGE_TYPE = `connector_request_body_bytes`; +export const CONNECTOR_USAGE_REPORTING_SOURCE_ID = `task-connector-usage-report`; +export const MAX_PUSH_ATTEMPTS = 5; + +export class ConnectorUsageReportingTask { + private readonly logger: Logger; + private readonly eventLogIndex: string; + private readonly projectId: string | undefined; + private readonly caCertificate: string | undefined; + private readonly usageApiUrl: string; + + constructor({ + logger, + eventLogIndex, + core, + taskManager, + projectId, + config, + }: { + logger: Logger; + eventLogIndex: string; + core: CoreSetup; + taskManager: TaskManagerSetupContract; + projectId: string | undefined; + config: ActionsConfig['usage']; + }) { + this.logger = logger; + this.projectId = projectId; + this.eventLogIndex = eventLogIndex; + this.usageApiUrl = config.url; + const caCertificatePath = config.ca?.path; + + if (caCertificatePath && caCertificatePath.length > 0) { + try { + this.caCertificate = fs.readFileSync(caCertificatePath, 'utf8'); + } catch (e) { + this.caCertificate = undefined; + this.logger.error( + `CA Certificate for the project "${projectId}" couldn't be loaded, Error: ${e.message}` + ); + } + } + + taskManager.registerTaskDefinitions({ + [CONNECTOR_USAGE_REPORTING_TASK_TYPE]: { + title: 'Connector usage reporting task', + timeout: '1m', + createTaskRunner: ({ taskInstance }: { taskInstance: ConcreteTaskInstance }) => { + return { + run: async () => this.runTask(taskInstance, core), + cancel: async () => {}, + }; + }, + }, + }); + } + + public start = async (taskManager?: TaskManagerStartContract) => { + if (!taskManager) { + this.logger.error( + `Missing required task manager service during start of ${CONNECTOR_USAGE_REPORTING_TASK_TYPE}` + ); + return; + } + + try { + await taskManager.ensureScheduled({ + id: CONNECTOR_USAGE_REPORTING_TASK_ID, + taskType: CONNECTOR_USAGE_REPORTING_TASK_TYPE, + schedule: { + ...CONNECTOR_USAGE_REPORTING_TASK_SCHEDULE, + }, + state: {}, + params: {}, + }); + } catch (e) { + this.logger.error( + `Error scheduling task ${CONNECTOR_USAGE_REPORTING_TASK_TYPE}, received ${e.message}` + ); + } + }; + + private runTask = async (taskInstance: ConcreteTaskInstance, core: CoreSetup) => { + const { state } = taskInstance; + + if (!this.projectId) { + this.logger.warn( + `Missing required project id while running ${CONNECTOR_USAGE_REPORTING_TASK_TYPE}` + ); + return { + state, + }; + } + + if (!this.caCertificate) { + this.logger.error( + `Missing required CA Certificate while running ${CONNECTOR_USAGE_REPORTING_TASK_TYPE}` + ); + return { + state, + }; + } + + const [{ elasticsearch }] = await core.getStartServices(); + const esClient = elasticsearch.client.asInternalUser; + + const now = new Date(); + const oneDayAgo = new Date(new Date().getTime() - 24 * 60 * 60 * 1000); + const lastReportedUsageDate: Date = !!state.lastReportedUsageDate + ? new Date(state.lastReportedUsageDate) + : oneDayAgo; + + let attempts: number = state.attempts || 0; + + const fromDate = lastReportedUsageDate; + const toDate = now; + + let totalUsage = 0; + try { + totalUsage = await this.getTotalUsage({ + esClient, + fromDate, + toDate, + }); + } catch (e) { + this.logger.error(`Usage data could not be fetched. It will be retried. Error:${e.message}`); + return { + state: { + lastReportedUsageDate, + attempts, + }, + runAt: now, + }; + } + + const record: ConnectorUsageReport = this.createUsageRecord({ + totalUsage, + fromDate, + toDate, + projectId: this.projectId, + }); + + this.logger.debug(`Record: ${JSON.stringify(record)}`); + + try { + attempts = attempts + 1; + await this.pushUsageRecord(record); + this.logger.info( + `Connector usage record has been successfully reported, ${record.creation_timestamp}, usage: ${record.usage.quantity}, period:${record.usage.period_seconds}` + ); + } catch (e) { + if (attempts < MAX_PUSH_ATTEMPTS) { + this.logger.error( + `Usage data could not be pushed to usage-api. It will be retried (${attempts}). Error:${e.message}` + ); + + return { + state: { + lastReportedUsageDate, + attempts, + }, + runAt: this.getDelayedRetryDate({ attempts, now }), + }; + } + this.logger.error( + `Usage data could not be pushed to usage-api. Stopped retrying after ${attempts} attempts. Error:${e.message}` + ); + return { + state: { + lastReportedUsageDate, + attempts: 0, + }, + }; + } + + return { + state: { lastReportedUsageDate: toDate, attempts: 0 }, + }; + }; + + private getTotalUsage = async ({ + esClient, + fromDate, + toDate, + }: { + esClient: ElasticsearchClient; + fromDate: Date; + toDate: Date; + }): Promise => { + const usageResult = await esClient.search({ + index: this.eventLogIndex, + sort: '@timestamp', + size: 0, + query: { + bool: { + filter: { + bool: { + must: [ + { + term: { 'event.action': 'execute' }, + }, + { + term: { 'event.provider': 'actions' }, + }, + { + exists: { + field: 'kibana.action.execution.usage.request_body_bytes', + }, + }, + { + range: { + '@timestamp': { + gt: fromDate, + lte: toDate, + }, + }, + }, + ], + }, + }, + }, + }, + aggs: { + total_usage: { sum: { field: 'kibana.action.execution.usage.request_body_bytes' } }, + }, + }); + + return (usageResult.aggregations?.total_usage as AggregationsSumAggregate)?.value ?? 0; + }; + + private createUsageRecord = ({ + totalUsage, + fromDate, + toDate, + projectId, + }: { + totalUsage: number; + fromDate: Date; + toDate: Date; + projectId: string; + }): ConnectorUsageReport => { + const period = Math.round((toDate.getTime() - fromDate.getTime()) / 1000); + const toStr = toDate.toISOString(); + const timestamp = new Date(toStr); + timestamp.setMinutes(0); + timestamp.setSeconds(0); + timestamp.setMilliseconds(0); + + return { + id: `connector-request-body-bytes-${projectId}-${timestamp.toISOString()}`, + usage_timestamp: toStr, + creation_timestamp: toStr, + usage: { + type: CONNECTOR_USAGE_TYPE, + period_seconds: period, + quantity: totalUsage, + }, + source: { + id: CONNECTOR_USAGE_REPORTING_SOURCE_ID, + instance_group_id: projectId, + }, + }; + }; + + private pushUsageRecord = async (record: ConnectorUsageReport) => { + return axios.post(this.usageApiUrl, [record], { + headers: { 'Content-Type': 'application/json' }, + timeout: CONNECTOR_USAGE_REPORTING_TASK_TIMEOUT, + httpsAgent: new https.Agent({ + ca: this.caCertificate, + }), + }); + }; + + private getDelayedRetryDate = ({ attempts, now }: { attempts: number; now: Date }) => { + const baseDelay = 60 * 1000; + const delayByAttempts = baseDelay * attempts; + + const delayedTime = now.getTime() + delayByAttempts; + + return new Date(delayedTime); + }; +} diff --git a/x-pack/plugins/actions/server/usage/types.ts b/x-pack/plugins/actions/server/usage/types.ts index 6bdfe316c76e2..d57de6f4dad33 100644 --- a/x-pack/plugins/actions/server/usage/types.ts +++ b/x-pack/plugins/actions/server/usage/types.ts @@ -65,3 +65,18 @@ export const byServiceProviderTypeSchema: MakeSchemaFrom['count_ac other: { type: 'long' }, ses: { type: 'long' }, }; + +export interface ConnectorUsageReport { + id: string; + usage_timestamp: string; + creation_timestamp: string; + usage: { + type: string; + period_seconds: number; + quantity: number | string | undefined; + }; + source: { + id: string | undefined; + instance_group_id: string; + }; +} diff --git a/x-pack/plugins/actions/tsconfig.json b/x-pack/plugins/actions/tsconfig.json index d060287d24143..8a3c56a472064 100644 --- a/x-pack/plugins/actions/tsconfig.json +++ b/x-pack/plugins/actions/tsconfig.json @@ -24,7 +24,6 @@ "@kbn/i18n", "@kbn/utility-types", "@kbn/config-schema", - "@kbn/config", "@kbn/core-saved-objects-server", "@kbn/es-query", "@kbn/apm-utils", @@ -47,7 +46,8 @@ "@kbn/core-http-server", "@kbn/core-test-helpers-kbn-server", "@kbn/security-plugin-types-server", - "@kbn/core-application-common" + "@kbn/core-application-common", + "@kbn/cloud-plugin" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/fields_config.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/fields_config.tsx index 38b5620465a0d..f967fffd45647 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/fields_config.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/fields_config.tsx @@ -261,7 +261,7 @@ const FieldPanel: FC = ({ disabled: removeDisabled, }, ], - 'data=test-subj': 'aiopsChangePointDetectionContextMenuPanel', + 'data-test-subj': 'aiopsChangePointDetectionContextMenuPanel', }, { id: 'attachMainPanel', @@ -638,7 +638,7 @@ export const FieldsControls: FC> = ({ }) => { const { splitFieldsOptions, combinedQuery } = useChangePointDetectionContext(); const { dataView } = useDataSource(); - const { data, uiSettings, fieldFormats, charts, fieldStats } = useAiopsAppContext(); + const { data, uiSettings, fieldFormats, charts, fieldStats, theme } = useAiopsAppContext(); const timefilter = useTimefilter(); // required in order to trigger state updates useTimeRangeUpdates(); @@ -677,6 +677,7 @@ export const FieldsControls: FC> = ({ } : undefined } + theme={theme} > diff --git a/x-pack/plugins/aiops/public/components/document_count_content/document_count_content/document_count_content.tsx b/x-pack/plugins/aiops/public/components/document_count_content/document_count_content/document_count_content.tsx index 23caf21c39ee3..4dbf021e3b10b 100644 --- a/x-pack/plugins/aiops/public/components/document_count_content/document_count_content/document_count_content.tsx +++ b/x-pack/plugins/aiops/public/components/document_count_content/document_count_content/document_count_content.tsx @@ -15,6 +15,7 @@ import type { import { useAppSelector } from '@kbn/aiops-log-rate-analysis/state'; import { DocumentCountChartRedux } from '@kbn/aiops-components'; +import { AIOPS_EMBEDDABLE_ORIGIN } from '@kbn/aiops-common/constants'; import { useAiopsAppContext } from '../../../hooks/use_aiops_app_context'; @@ -37,17 +38,29 @@ export const DocumentCountContent: FC = ({ barHighlightColorOverride, ...docCountChartProps }) => { - const { data, uiSettings, fieldFormats, charts } = useAiopsAppContext(); + const { data, uiSettings, fieldFormats, charts, embeddingOrigin } = useAiopsAppContext(); const { documentStats } = useAppSelector((s) => s.logRateAnalysis); const { sampleProbability, totalCount, documentCountStats } = documentStats; if (documentCountStats === undefined) { - return totalCount !== undefined ? ( + return totalCount !== undefined && embeddingOrigin !== AIOPS_EMBEDDABLE_ORIGIN.DASHBOARD ? ( ) : null; } + if (embeddingOrigin === AIOPS_EMBEDDABLE_ORIGIN.DASHBOARD) { + return ( + + ); + } + return ( 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 7bf43037f45c0..2821b59353b52 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 @@ -7,7 +7,7 @@ import { isEqual } from 'lodash'; import React, { useCallback, useEffect, useMemo, useRef, type FC } from 'react'; -import { EuiButton, EuiEmptyPrompt, EuiHorizontalRule, EuiPanel } from '@elastic/eui'; +import { EuiButton, EuiEmptyPrompt, EuiSpacer, EuiPanel } from '@elastic/eui'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { BarStyleAccessor } from '@elastic/charts/dist/chart_types/xy_chart/utils/specs'; @@ -28,13 +28,16 @@ import { setInitialAnalysisStart, useAppDispatch, useAppSelector, + setGroupResults, } from '@kbn/aiops-log-rate-analysis/state'; +import { AIOPS_EMBEDDABLE_ORIGIN } from '@kbn/aiops-common/constants'; import { DocumentCountContent } from '../../document_count_content/document_count_content'; import { LogRateAnalysisResults, type LogRateAnalysisResultsData, } from '../log_rate_analysis_results'; +import { useAiopsAppContext } from '../../../hooks/use_aiops_app_context'; export const DEFAULT_SEARCH_QUERY: estypes.QueryDslQueryContainer = { match_all: {} }; const DEFAULT_SEARCH_BAR_QUERY: estypes.QueryDslQueryContainer = { @@ -69,9 +72,11 @@ export const LogRateAnalysisContent: FC = ({ onAnalysisCompleted, onWindowParametersChange, }) => { + const { embeddingOrigin } = useAiopsAppContext(); + const dispatch = useAppDispatch(); - const isRunning = useAppSelector((s) => s.logRateAnalysisStream.isRunning); + const isRunning = useAppSelector((s) => s.stream.isRunning); const significantItems = useAppSelector((s) => s.logRateAnalysisResults.significantItems); const significantItemsGroups = useAppSelector( (s) => s.logRateAnalysisResults.significantItemsGroups @@ -116,6 +121,7 @@ export const LogRateAnalysisContent: FC = ({ const { documentCountStats } = documentStats; function clearSelectionHandler() { + dispatch(setGroupResults(false)); dispatch(clearSelection()); dispatch(clearAllRowState()); } @@ -200,7 +206,11 @@ export const LogRateAnalysisContent: FC = ({ const changePointType = documentCountStats?.changePoint?.type; return ( - + {showDocumentCountContent && ( = ({ barStyleAccessor={barStyleAccessor} /> )} - + {showLogRateAnalysisResults && ( = ({ + timeRange, +}) => { + const { uiSettings } = useAiopsAppContext(); + const { dataView } = useDataSource(); + const { filters, query } = useFilterQueryUpdates(); + const appState = getDefaultLogRateAnalysisAppState({ + searchQuery: buildEsQuery( + dataView, + query ?? [], + filters ?? [], + uiSettings ? getEsQueryConfig(uiSettings) : undefined + ), + filters, + }); + const { searchQuery } = useSearch({ dataView, savedSearch: null }, appState, true); + + const timeRangeParsed = useMemo(() => { + if (timeRange) { + const min = datemath.parse(timeRange.from); + const max = datemath.parse(timeRange.to); + if (min && max) { + return { min, max }; + } + } + }, [timeRange]); + + return ( + <> + + + + ); +}; diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_options.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_options.tsx new file mode 100644 index 0000000000000..0aba8e2d763d4 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_options.tsx @@ -0,0 +1,190 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { FC } from 'react'; +import React from 'react'; + +import { EuiButtonGroup, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import { + clearAllRowState, + setGroupResults, + useAppDispatch, + useAppSelector, +} from '@kbn/aiops-log-rate-analysis/state'; +import { + commonColumns, + significantItemColumns, + setSkippedColumns, + type LogRateAnalysisResultsTableColumnName, +} from '@kbn/aiops-log-rate-analysis/state/log_rate_analysis_table_slice'; +import { setCurrentFieldFilterSkippedItems } from '@kbn/aiops-log-rate-analysis/state/log_rate_analysis_field_candidates_slice'; + +import { ItemFilterPopover as FieldFilterPopover } from './item_filter_popover'; +import { ItemFilterPopover as ColumnFilterPopover } from './item_filter_popover'; + +const groupResultsMessage = i18n.translate( + 'xpack.aiops.logRateAnalysis.resultsTable.groupedSwitchLabel.groupResults', + { + defaultMessage: 'Smart grouping', + } +); +const fieldFilterHelpText = i18n.translate('xpack.aiops.logRateAnalysis.page.fieldFilterHelpText', { + defaultMessage: + 'Deselect non-relevant fields to remove them from the analysis and click the Apply button to rerun the analysis. Use the search bar to filter the list, then select/deselect multiple fields with the actions below.', +}); +const columnsFilterHelpText = i18n.translate( + 'xpack.aiops.logRateAnalysis.page.columnsFilterHelpText', + { + defaultMessage: 'Configure visible columns.', + } +); +const disabledFieldFilterApplyButtonTooltipContent = i18n.translate( + 'xpack.aiops.analysis.fieldSelectorNotEnoughFieldsSelected', + { + defaultMessage: 'Grouping requires at least 2 fields to be selected.', + } +); +const disabledColumnFilterApplyButtonTooltipContent = i18n.translate( + 'xpack.aiops.analysis.columnSelectorNotEnoughColumnsSelected', + { + defaultMessage: 'At least one column must be selected.', + } +); +const columnSearchAriaLabel = i18n.translate('xpack.aiops.analysis.columnSelectorAriaLabel', { + defaultMessage: 'Filter columns', +}); +const columnsButton = i18n.translate('xpack.aiops.logRateAnalysis.page.columnsFilterButtonLabel', { + defaultMessage: 'Columns', +}); +const fieldsButton = i18n.translate('xpack.aiops.analysis.fieldsButtonLabel', { + defaultMessage: 'Fields', +}); +const groupResultsOffMessage = i18n.translate( + 'xpack.aiops.logRateAnalysis.resultsTable.groupedSwitchLabel.groupResultsOff', + { + defaultMessage: 'Off', + } +); +const groupResultsOnMessage = i18n.translate( + 'xpack.aiops.logRateAnalysis.resultsTable.groupedSwitchLabel.groupResultsOn', + { + defaultMessage: 'On', + } +); +const resultsGroupedOffId = 'aiopsLogRateAnalysisGroupingOff'; +const resultsGroupedOnId = 'aiopsLogRateAnalysisGroupingOn'; + +export interface LogRateAnalysisOptionsProps { + foundGroups: boolean; + growFirstItem?: boolean; +} + +export const LogRateAnalysisOptions: FC = ({ + foundGroups, + growFirstItem = false, +}) => { + const dispatch = useAppDispatch(); + + const { groupResults } = useAppSelector((s) => s.logRateAnalysis); + const { isRunning } = useAppSelector((s) => s.stream); + const fieldCandidates = useAppSelector((s) => s.logRateAnalysisFieldCandidates); + const { skippedColumns } = useAppSelector((s) => s.logRateAnalysisTable); + const { fieldFilterUniqueItems, initialFieldFilterSkippedItems } = fieldCandidates; + const fieldFilterButtonDisabled = + isRunning || fieldCandidates.isLoading || fieldFilterUniqueItems.length === 0; + const toggleIdSelected = groupResults ? resultsGroupedOnId : resultsGroupedOffId; + + const onGroupResultsToggle = (optionId: string) => { + dispatch(setGroupResults(optionId === resultsGroupedOnId)); + // When toggling the group switch, clear all row selections + dispatch(clearAllRowState()); + }; + + const onVisibleColumnsChange = (columns: LogRateAnalysisResultsTableColumnName[]) => { + dispatch(setSkippedColumns(columns)); + }; + + const onFieldsFilterChange = (skippedFieldsUpdate: string[]) => { + dispatch(setCurrentFieldFilterSkippedItems(skippedFieldsUpdate)); + }; + + // Disable the grouping switch toggle only if no groups were found, + // the toggle wasn't enabled already and no fields were selected to be skipped. + const disabledGroupResultsSwitch = !foundGroups && !groupResults; + + const toggleButtons = [ + { + id: resultsGroupedOffId, + label: groupResultsOffMessage, + 'data-test-subj': 'aiopsLogRateAnalysisGroupSwitchOff', + }, + { + id: resultsGroupedOnId, + label: groupResultsOnMessage, + 'data-test-subj': 'aiopsLogRateAnalysisGroupSwitchOn', + }, + ]; + + return ( + <> + + + + {groupResultsMessage} + + + + + + + + + + + void} + /> + + + ); +}; 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 b97019c4b4d29..1eb4f8fd0af0d 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 @@ -11,12 +11,13 @@ import { isEqual } from 'lodash'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { + EuiButtonIcon, EuiButton, - EuiButtonGroup, EuiCallOut, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, + EuiToolTip, EuiSpacer, EuiText, } from '@elastic/eui'; @@ -26,6 +27,7 @@ import { ProgressControls } from '@kbn/aiops-components'; import { cancelStream, startStream } from '@kbn/ml-response-stream/client'; import { clearAllRowState, + setGroupResults, useAppDispatch, useAppSelector, } from '@kbn/aiops-log-rate-analysis/state'; @@ -37,8 +39,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import type { SignificantItem, SignificantItemGroup } from '@kbn/ml-agg-utils'; -import { useStorage } from '@kbn/ml-local-storage'; -import { AIOPS_ANALYSIS_RUN_ORIGIN } from '@kbn/aiops-common/constants'; +import { AIOPS_ANALYSIS_RUN_ORIGIN, AIOPS_EMBEDDABLE_ORIGIN } from '@kbn/aiops-common/constants'; import type { AiopsLogRateAnalysisSchema } from '@kbn/aiops-log-rate-analysis/api/schema'; import type { AiopsLogRateAnalysisSchemaSignificantItem } from '@kbn/aiops-log-rate-analysis/api/schema_v3'; import { @@ -50,15 +51,6 @@ import { fetchFieldCandidates } from '@kbn/aiops-log-rate-analysis/state/log_rat import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { useDataSource } from '../../hooks/use_data_source'; -import { - commonColumns, - significantItemColumns, -} from '../log_rate_analysis_results_table/use_columns'; -import { - AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS, - type AiOpsKey, - type AiOpsStorageMapped, -} from '../../types/storage'; import { getGroupTableItems, @@ -66,68 +58,15 @@ import { LogRateAnalysisResultsGroupsTable, } from '../log_rate_analysis_results_table'; -import { ItemFilterPopover as FieldFilterPopover } from './item_filter_popover'; -import { ItemFilterPopover as ColumnFilterPopover } from './item_filter_popover'; import { LogRateAnalysisInfoPopover } from './log_rate_analysis_info_popover'; -import type { ColumnNames } from '../log_rate_analysis_results_table'; +import { LogRateAnalysisOptions } from './log_rate_analysis_options'; -const groupResultsMessage = i18n.translate( - 'xpack.aiops.logRateAnalysis.resultsTable.groupedSwitchLabel.groupResults', - { - defaultMessage: 'Smart grouping', - } -); const groupResultsHelpMessage = i18n.translate( 'xpack.aiops.logRateAnalysis.resultsTable.groupedSwitchLabel.groupResultsHelpMessage', { defaultMessage: 'Items which are unique to a group are marked by an asterisk (*).', } ); -const groupResultsOffMessage = i18n.translate( - 'xpack.aiops.logRateAnalysis.resultsTable.groupedSwitchLabel.groupResultsOff', - { - defaultMessage: 'Off', - } -); -const groupResultsOnMessage = i18n.translate( - 'xpack.aiops.logRateAnalysis.resultsTable.groupedSwitchLabel.groupResultsOn', - { - defaultMessage: 'On', - } -); -const resultsGroupedOffId = 'aiopsLogRateAnalysisGroupingOff'; -const resultsGroupedOnId = 'aiopsLogRateAnalysisGroupingOn'; -const fieldFilterHelpText = i18n.translate('xpack.aiops.logRateAnalysis.page.fieldFilterHelpText', { - defaultMessage: - 'Deselect non-relevant fields to remove them from the analysis and click the Apply button to rerun the analysis. Use the search bar to filter the list, then select/deselect multiple fields with the actions below.', -}); -const columnsFilterHelpText = i18n.translate( - 'xpack.aiops.logRateAnalysis.page.columnsFilterHelpText', - { - defaultMessage: 'Configure visible columns.', - } -); -const disabledFieldFilterApplyButtonTooltipContent = i18n.translate( - 'xpack.aiops.analysis.fieldSelectorNotEnoughFieldsSelected', - { - defaultMessage: 'Grouping requires at least 2 fields to be selected.', - } -); -const disabledColumnFilterApplyButtonTooltipContent = i18n.translate( - 'xpack.aiops.analysis.columnSelectorNotEnoughColumnsSelected', - { - defaultMessage: 'At least one column must be selected.', - } -); -const columnSearchAriaLabel = i18n.translate('xpack.aiops.analysis.columnSelectorAriaLabel', { - defaultMessage: 'Filter columns', -}); -const columnsButton = i18n.translate('xpack.aiops.logRateAnalysis.page.columnsFilterButtonLabel', { - defaultMessage: 'Columns', -}); -const fieldsButton = i18n.translate('xpack.aiops.analysis.fieldsButtonLabel', { - defaultMessage: 'Fields', -}); /** * Interface for log rate analysis results data. @@ -173,72 +112,51 @@ export const LogRateAnalysisResults: FC = ({ documentStats: { sampleProbability }, stickyHistogram, isBrushCleared, + groupResults, } = useAppSelector((s) => s.logRateAnalysis); - const { isRunning, errors: streamErrors } = useAppSelector((s) => s.logRateAnalysisStream); + const { isRunning, errors: streamErrors } = useAppSelector((s) => s.stream); const data = useAppSelector((s) => s.logRateAnalysisResults); const fieldCandidates = useAppSelector((s) => s.logRateAnalysisFieldCandidates); + const { skippedColumns } = useAppSelector((s) => s.logRateAnalysisTable); const { currentAnalysisWindowParameters } = data; // Store the performance metric's start time using a ref // to be able to track it across rerenders. const analysisStartTime = useRef(window.performance.now()); const abortCtrl = useRef(new AbortController()); + const previousSearchQuery = useRef(searchQuery); - const [groupResults, setGroupResults] = useState(false); const [overrides, setOverrides] = useState( undefined ); const [shouldStart, setShouldStart] = useState(false); - const [toggleIdSelected, setToggleIdSelected] = useState(resultsGroupedOffId); - const [skippedColumns, setSkippedColumns] = useStorage< - AiOpsKey, - AiOpsStorageMapped - >(AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS, ['p-value', 'Baseline rate', 'Deviation rate']); - // null is used as the uninitialized state to identify the first load. - const [skippedFields, setSkippedFields] = useState(null); - - const onGroupResultsToggle = (optionId: string) => { - setToggleIdSelected(optionId); - setGroupResults(optionId === resultsGroupedOnId); - - // When toggling the group switch, clear all row selections - dispatch(clearAllRowState()); + const [embeddableOptionsVisible, setEmbeddableOptionsVisible] = useState(false); + + const onEmbeddableOptionsClickHandler = () => { + setEmbeddableOptionsVisible((s) => !s); }; - const { - fieldFilterUniqueItems, - fieldFilterSkippedItems, - keywordFieldCandidates, - textFieldCandidates, - } = fieldCandidates; - const fieldFilterButtonDisabled = - isRunning || fieldCandidates.isLoading || fieldFilterUniqueItems.length === 0; - - // Set skipped fields only on first load, otherwise we'd overwrite the user's selection. + const { currentFieldFilterSkippedItems, keywordFieldCandidates, textFieldCandidates } = + fieldCandidates; + useEffect(() => { - if (skippedFields === null && fieldFilterSkippedItems.length > 0) - setSkippedFields(fieldFilterSkippedItems); - }, [fieldFilterSkippedItems, skippedFields]); + if (currentFieldFilterSkippedItems === null) return; - const onFieldsFilterChange = (skippedFieldsUpdate: string[]) => { dispatch(resetResults()); - setSkippedFields(skippedFieldsUpdate); setOverrides({ loaded: 0, remainingKeywordFieldCandidates: keywordFieldCandidates.filter( - (d) => !skippedFieldsUpdate.includes(d) + (d) => !currentFieldFilterSkippedItems.includes(d) ), remainingTextFieldCandidates: textFieldCandidates.filter( - (d) => !skippedFieldsUpdate.includes(d) + (d) => !currentFieldFilterSkippedItems.includes(d) ), regroupOnly: false, }); startHandler(true, false); - }; - - const onVisibleColumnsChange = (columns: ColumnNames[]) => { - setSkippedColumns(columns); - }; + // custom check to trigger on currentFieldFilterSkippedItems change + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [currentFieldFilterSkippedItems]); function cancelHandler() { abortCtrl.current.abort(); @@ -298,18 +216,20 @@ export const LogRateAnalysisResults: FC = ({ dispatch(resetResults()); setOverrides({ remainingKeywordFieldCandidates: keywordFieldCandidates.filter( - (d) => skippedFields === null || !skippedFields.includes(d) + (d) => + currentFieldFilterSkippedItems === null || !currentFieldFilterSkippedItems.includes(d) ), remainingTextFieldCandidates: textFieldCandidates.filter( - (d) => skippedFields === null || !skippedFields.includes(d) + (d) => + currentFieldFilterSkippedItems === null || !currentFieldFilterSkippedItems.includes(d) ), }); } // Reset grouping to false and clear all row selections when restarting the analysis. if (resetGroupButton) { - setGroupResults(false); - setToggleIdSelected(resultsGroupedOffId); + dispatch(setGroupResults(false)); + // When toggling the group switch, clear all row selections dispatch(clearAllRowState()); } @@ -371,12 +291,13 @@ export const LogRateAnalysisResults: FC = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [shouldStart]); + // On mount, fetch field candidates first. Once they are populated, + // the actual analysis will be triggered. useEffect(() => { if (startParams) { dispatch(fetchFieldCandidates(startParams)); dispatch(setCurrentAnalysisType(analysisType)); dispatch(setCurrentAnalysisWindowParameters(chartWindowParameters)); - dispatch(startStream(startParams)); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -386,6 +307,19 @@ export const LogRateAnalysisResults: FC = ({ [data.significantItemsGroups] ); + const searchQueryUpdated = useMemo(() => { + let searchQueryChanged = false; + if ( + !isRunning && + previousSearchQuery.current !== undefined && + !isEqual(previousSearchQuery.current, searchQuery) + ) { + searchQueryChanged = true; + } + previousSearchQuery.current = searchQuery; + return searchQueryChanged; + }, [searchQuery, isRunning]); + const shouldRerunAnalysis = useMemo( () => currentAnalysisWindowParameters !== undefined && @@ -399,23 +333,6 @@ export const LogRateAnalysisResults: FC = ({ }, 0); const foundGroups = groupTableItems.length > 0 && groupItemCount > 0; - // Disable the grouping switch toggle only if no groups were found, - // the toggle wasn't enabled already and no fields were selected to be skipped. - const disabledGroupResultsSwitch = !foundGroups && !groupResults; - - const toggleButtons = [ - { - id: resultsGroupedOffId, - label: groupResultsOffMessage, - 'data-test-subj': 'aiopsLogRateAnalysisGroupSwitchOff', - }, - { - id: resultsGroupedOnId, - label: groupResultsOnMessage, - 'data-test-subj': 'aiopsLogRateAnalysisGroupSwitchOn', - }, - ]; - return (
= ({ onRefresh={() => startHandler(false)} onCancel={cancelHandler} onReset={onReset} - shouldRerunAnalysis={shouldRerunAnalysis} + shouldRerunAnalysis={shouldRerunAnalysis || searchQueryUpdated} analysisInfo={} > - - - - {groupResultsMessage} - + <> + {embeddingOrigin !== AIOPS_EMBEDDABLE_ORIGIN.DASHBOARD && ( + + )} + {embeddingOrigin === AIOPS_EMBEDDABLE_ORIGIN.DASHBOARD && ( - + + + - - - - - - - void} - /> - + )} + + {embeddingOrigin === AIOPS_EMBEDDABLE_ORIGIN.DASHBOARD && embeddableOptionsVisible && ( + <> + + + + + + )} + {errors.length > 0 ? ( <> diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/index.ts b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/index.ts index 6813e71704918..c5112723e2784 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/index.ts +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/index.ts @@ -8,4 +8,3 @@ export { getGroupTableItems } from './get_group_table_items'; export { LogRateAnalysisResultsTable } from './log_rate_analysis_results_table'; export { LogRateAnalysisResultsGroupsTable } from './log_rate_analysis_results_table_groups'; -export type { ColumnNames } from './use_columns'; 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 83be306e93f50..e9072c2929f14 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 @@ -83,13 +83,11 @@ export const LogRateAnalysisResultsTable: FC = }, [allSignificantItems, groupFilter]); const zeroDocsFallback = useAppSelector((s) => s.logRateAnalysisResults.zeroDocsFallback); - const pinnedGroup = useAppSelector((s) => s.logRateAnalysisTableRow.pinnedGroup); - const selectedGroup = useAppSelector((s) => s.logRateAnalysisTableRow.selectedGroup); - const pinnedSignificantItem = useAppSelector( - (s) => s.logRateAnalysisTableRow.pinnedSignificantItem - ); + const pinnedGroup = useAppSelector((s) => s.logRateAnalysisTable.pinnedGroup); + const selectedGroup = useAppSelector((s) => s.logRateAnalysisTable.selectedGroup); + const pinnedSignificantItem = useAppSelector((s) => s.logRateAnalysisTable.pinnedSignificantItem); const selectedSignificantItem = useAppSelector( - (s) => s.logRateAnalysisTableRow.selectedSignificantItem + (s) => s.logRateAnalysisTable.selectedSignificantItem ); const dispatch = useAppDispatch(); 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 d69a0fec7200f..6bd0a5e4ce213 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 @@ -91,8 +91,8 @@ export const LogRateAnalysisResultsGroupsTable: FC s.logRateAnalysisTableRow.pinnedGroup); - const selectedGroup = useAppSelector((s) => s.logRateAnalysisTableRow.selectedGroup); + const pinnedGroup = useAppSelector((s) => s.logRateAnalysisTable.pinnedGroup); + const selectedGroup = useAppSelector((s) => s.logRateAnalysisTable.selectedGroup); const dispatch = useAppDispatch(); const isMounted = useMountedState(); diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/use_columns.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/use_columns.tsx index f3b8195767101..c5b7a83e33641 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/use_columns.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/use_columns.tsx @@ -21,6 +21,11 @@ import { type SignificantItem, SIGNIFICANT_ITEM_TYPE } from '@kbn/ml-agg-utils'; import { getCategoryQuery } from '@kbn/aiops-log-pattern-analysis/get_category_query'; import type { FieldStatsServices } from '@kbn/unified-field-list/src/components/field_stats'; import { useAppSelector } from '@kbn/aiops-log-rate-analysis/state'; +import { + commonColumns, + significantItemColumns, + type LogRateAnalysisResultsTableColumnName, +} from '@kbn/aiops-log-rate-analysis/state/log_rate_analysis_table_slice'; import { getBaselineAndDeviationRates, getLogRateChange, @@ -40,55 +45,6 @@ const TRUNCATE_TEXT_LINES = 3; const UNIQUE_COLUMN_WIDTH = '40px'; const NOT_AVAILABLE = '--'; -export const commonColumns = { - ['Log rate']: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.logRateColumnTitle', { - defaultMessage: 'Log rate', - }), - ['Doc count']: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.docCountColumnTitle', { - defaultMessage: 'Doc count', - }), - ['p-value']: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.pValueColumnTitle', { - defaultMessage: 'p-value', - }), - ['Impact']: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.impactColumnTitle', { - defaultMessage: 'Impact', - }), - ['Baseline rate']: i18n.translate( - 'xpack.aiops.logRateAnalysis.resultsTable.baselineRateColumnTitle', - { - defaultMessage: 'Baseline rate', - } - ), - ['Deviation rate']: i18n.translate( - 'xpack.aiops.logRateAnalysis.resultsTable.deviationRateColumnTitle', - { - defaultMessage: 'Deviation rate', - } - ), - ['Log rate change']: i18n.translate( - 'xpack.aiops.logRateAnalysis.resultsTable.logRateChangeColumnTitle', - { - defaultMessage: 'Log rate change', - } - ), - ['Actions']: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.actionsColumnTitle', { - defaultMessage: 'Actions', - }), -}; - -export const significantItemColumns = { - ['Field name']: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.fieldNameColumnTitle', { - defaultMessage: 'Field name', - }), - ['Field value']: i18n.translate( - 'xpack.aiops.logRateAnalysis.resultsTable.fieldValueColumnTitle', - { - defaultMessage: 'Field value', - } - ), - ...commonColumns, -} as const; - export const LOG_RATE_ANALYSIS_RESULTS_TABLE_TYPE = { GROUPS: 'groups', SIGNIFICANT_ITEMS: 'significantItems', @@ -96,8 +52,6 @@ export const LOG_RATE_ANALYSIS_RESULTS_TABLE_TYPE = { export type LogRateAnalysisResultsTableType = (typeof LOG_RATE_ANALYSIS_RESULTS_TABLE_TYPE)[keyof typeof LOG_RATE_ANALYSIS_RESULTS_TABLE_TYPE]; -export type ColumnNames = keyof typeof significantItemColumns | 'unique'; - const logRateHelpMessage = i18n.translate( 'xpack.aiops.logRateAnalysis.resultsTable.logRateColumnTooltip', { @@ -213,7 +167,7 @@ export const useColumns = ( const { earliest, latest } = useAppSelector((s) => s.logRateAnalysis); const timeRangeMs = { from: earliest ?? 0, to: latest ?? 0 }; - const loading = useAppSelector((s) => s.logRateAnalysisStream.isRunning); + const loading = useAppSelector((s) => s.stream.isRunning); const zeroDocsFallback = useAppSelector((s) => s.logRateAnalysisResults.zeroDocsFallback); const { documentStats: { documentCountStats }, @@ -271,7 +225,10 @@ export const useColumns = ( [currentAnalysisType, buckets] ); - const columnsMap: Record> = useMemo( + const columnsMap: Record< + LogRateAnalysisResultsTableColumnName, + EuiBasicTableColumn + > = useMemo( () => ({ ['Field name']: { 'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnFieldName', @@ -615,20 +572,21 @@ export const useColumns = ( ); const columns = useMemo(() => { - const columnNamesToReturn: Partial> = isGroupsTable - ? commonColumns - : significantItemColumns; + const columnNamesToReturn: Partial> = + isGroupsTable ? commonColumns : significantItemColumns; const columnsToReturn = []; for (const columnName in columnNamesToReturn) { if ( Object.hasOwn(columnNamesToReturn, columnName) === false || - skippedColumns.includes(columnNamesToReturn[columnName as ColumnNames] as string) || + skippedColumns.includes( + columnNamesToReturn[columnName as LogRateAnalysisResultsTableColumnName] as string + ) || ((columnName === 'p-value' || columnName === 'Impact') && zeroDocsFallback) ) continue; - columnsToReturn.push(columnsMap[columnName as ColumnNames]); + columnsToReturn.push(columnsMap[columnName as LogRateAnalysisResultsTableColumnName]); } if (isExpandedRow === true) { diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/use_view_in_discover_action.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/use_view_in_discover_action.tsx index ec4284d6452e5..765f1435a93ad 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/use_view_in_discover_action.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/use_view_in_discover_action.tsx @@ -28,8 +28,8 @@ export const useViewInDiscoverAction = (dataViewId?: string): TableItemAction => const { application, share, data } = useAiopsAppContext(); const discoverLocator = useMemo( - () => share.url.locators.get('DISCOVER_APP_LOCATOR'), - [share.url.locators] + () => share?.url.locators.get('DISCOVER_APP_LOCATOR'), + [share?.url.locators] ); const discoverUrlError = useMemo(() => { diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/use_view_in_log_pattern_analysis_action.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/use_view_in_log_pattern_analysis_action.tsx index dbac6fbe8c9f3..ec1f6774b6b46 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/use_view_in_log_pattern_analysis_action.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/use_view_in_log_pattern_analysis_action.tsx @@ -32,7 +32,7 @@ const viewInLogPatternAnalysisMessage = i18n.translate( export const useViewInLogPatternAnalysisAction = (dataViewId?: string): TableItemAction => { const { application, share, data } = useAiopsAppContext(); - const mlLocator = useMemo(() => share.url.locators.get('ML_APP_LOCATOR'), [share.url.locators]); + const mlLocator = useMemo(() => share?.url.locators.get('ML_APP_LOCATOR'), [share?.url.locators]); const generateLogPatternAnalysisUrl = async ( groupTableItem: GroupTableItem | SignificantItem diff --git a/x-pack/plugins/aiops/public/components/page_header/page_header.tsx b/x-pack/plugins/aiops/public/components/page_header/page_header.tsx index 9895fe082fcc1..a01e715e86272 100644 --- a/x-pack/plugins/aiops/public/components/page_header/page_header.tsx +++ b/x-pack/plugins/aiops/public/components/page_header/page_header.tsx @@ -9,7 +9,7 @@ import { css } from '@emotion/react'; import type { FC } from 'react'; import React, { useCallback, useMemo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiPageHeader } from '@elastic/eui'; +import { EuiPageHeader } from '@elastic/eui'; import { useUrlState } from '@kbn/ml-url-state'; import { useStorage } from '@kbn/ml-local-storage'; @@ -71,29 +71,29 @@ export const PageHeader: FC = () => { return ( {dataView.getName()}
} + rightSideGroupProps={{ + gutterSize: 's', + 'data-test-subj': 'aiopsTimeRangeSelectorSection', + }} rightSideItems={[ - - {hasValidTimeField ? ( - - - - ) : null} - , + hasValidTimeField && ( + - , - ]} + ), + ].filter(Boolean)} /> ); }; diff --git a/x-pack/plugins/aiops/public/components/time_field_warning.tsx b/x-pack/plugins/aiops/public/components/time_field_warning.tsx new file mode 100644 index 0000000000000..beb64917af538 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/time_field_warning.tsx @@ -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 { EuiSpacer, EuiCallOut } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; + +export const TimeFieldWarning = () => { + return ( + <> + +

+ {i18n.translate('xpack.aiops.embeddableMenu.timeFieldWarning.title.description', { + defaultMessage: 'The analysis can only be run on data views with a time field.', + })} +

+
+ + + ); +}; diff --git a/x-pack/plugins/aiops/public/embeddables/change_point_chart/change_point_chart_initializer.tsx b/x-pack/plugins/aiops/public/embeddables/change_point_chart/change_point_chart_initializer.tsx index 08cf6ffd7dcb1..e69511fe45f92 100644 --- a/x-pack/plugins/aiops/public/embeddables/change_point_chart/change_point_chart_initializer.tsx +++ b/x-pack/plugins/aiops/public/embeddables/change_point_chart/change_point_chart_initializer.tsx @@ -18,16 +18,23 @@ import { EuiHorizontalRule, EuiTitle, } from '@elastic/eui'; +import { DatePickerContextProvider, type DatePickerDependencies } from '@kbn/ml-date-picker'; +import { UI_SETTINGS } from '@kbn/data-plugin/common'; +import type { FieldStatsServices } from '@kbn/unified-field-list/src/components/field_stats'; import { ES_FIELD_TYPES } from '@kbn/field-types'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { FieldStatsFlyoutProvider } from '@kbn/ml-field-stats-flyout'; +import { useTimefilter } from '@kbn/ml-date-picker'; import { pick } from 'lodash'; import type { FC } from 'react'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import usePrevious from 'react-use/lib/usePrevious'; import { + ChangePointDetectionContextProvider, ChangePointDetectionControlsContextProvider, + useChangePointDetectionContext, useChangePointDetectionControlsContext, } from '../../components/change_point_detection/change_point_detection_context'; import { DEFAULT_AGG_FUNCTION } from '../../components/change_point_detection/constants'; @@ -38,7 +45,8 @@ import { PartitionsSelector } from '../../components/change_point_detection/part import { SplitFieldSelector } from '../../components/change_point_detection/split_field_selector'; import { ViewTypeSelector } from '../../components/change_point_detection/view_type_selector'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; -import { DataSourceContextProvider } from '../../hooks/use_data_source'; +import { useDataSource, DataSourceContextProvider } from '../../hooks/use_data_source'; +import { FilterQueryContextProvider } from '../../hooks/use_filters_query'; import { DEFAULT_SERIES } from './const'; import type { ChangePointEmbeddableRuntimeState } from './types'; @@ -53,11 +61,18 @@ export const ChangePointChartInitializer: FC = ({ onCreate, onCancel, }) => { + const appContextValue = useAiopsAppContext(); const { + data: { dataViews }, unifiedSearch: { ui: { IndexPatternSelect }, }, - } = useAiopsAppContext(); + } = appContextValue; + + const datePickerDeps: DatePickerDependencies = { + ...pick(appContextValue, ['data', 'http', 'notifications', 'theme', 'uiSettings', 'i18n']), + uiSettingsKeys: UI_SETTINGS, + }; const [dataViewId, setDataViewId] = useState(initialInput?.dataViewId ?? ''); const [viewType, setViewType] = useState(initialInput?.viewType ?? 'charts'); @@ -135,15 +150,21 @@ export const ChangePointChartInitializer: FC = ({ }} /> - - - - - + + + + + + + + + + + @@ -190,7 +211,13 @@ export const FormControls: FC<{ onChange: (update: FormControlsProps) => void; onValidationChange: (isValid: boolean) => void; }> = ({ formInput, onChange, onValidationChange }) => { + const { charts, data, fieldFormats, theme, uiSettings } = useAiopsAppContext(); + const { dataView } = useDataSource(); + const { combinedQuery } = useChangePointDetectionContext(); const { metricFieldOptions, splitFieldsOptions } = useChangePointDetectionControlsContext(); + const timefilter = useTimefilter(); + const timefilterActiveBounds = timefilter.getActiveBounds(); + const prevMetricFieldOptions = usePrevious(metricFieldOptions); const enableSearch = useMemo(() => { @@ -238,10 +265,33 @@ export const FormControls: FC<{ [formInput, onChange] ); + const fieldStatsServices: FieldStatsServices = useMemo(() => { + return { + uiSettings, + dataViews: data.dataViews, + data, + fieldFormats, + charts, + }; + }, [uiSettings, data, fieldFormats, charts]); + if (!isPopulatedObject(formInput)) return null; return ( - <> + updateCallback({ maxSeriesToPlot: v })} onValidationChange={(result) => onValidationChange(result === null)} /> - + ); }; diff --git a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.tsx b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.tsx index 7cf39eb1cf4ae..2ce1a46780db1 100644 --- a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.tsx +++ b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.tsx @@ -11,7 +11,6 @@ import { } from '@kbn/aiops-change-point-detection/constants'; import type { Reference } from '@kbn/content-management-utils'; import type { StartServicesAccessor } from '@kbn/core-lifecycle-browser'; -import { type DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/common'; import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-views-plugin/common'; import type { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; @@ -38,32 +37,8 @@ import type { ChangePointEmbeddableState, } from './types'; -export interface EmbeddableChangePointChartStartServices { - data: DataPublicPluginStart; -} - export type EmbeddableChangePointChartType = typeof EMBEDDABLE_CHANGE_POINT_CHART_TYPE; -export const getDependencies = async ( - getStartServices: StartServicesAccessor -) => { - const [ - { http, uiSettings, notifications, ...startServices }, - { lens, data, usageCollection, fieldFormats }, - ] = await getStartServices(); - - return { - http, - uiSettings, - data, - notifications, - lens, - usageCollection, - fieldFormats, - ...startServices, - }; -}; - export const getChangePointChartEmbeddableFactory = ( getStartServices: StartServicesAccessor ) => { @@ -88,20 +63,6 @@ export const getChangePointChartEmbeddableFactory = ( buildEmbeddable: async (state, buildApi, uuid, parentApi) => { const [coreStart, pluginStart] = await getStartServices(); - const { http, uiSettings, notifications, ...startServices } = coreStart; - const { lens, data, usageCollection, fieldFormats } = pluginStart; - - const deps = { - http, - uiSettings, - data, - notifications, - lens, - usageCollection, - fieldFormats, - ...startServices, - }; - const { api: timeRangeApi, comparators: timeRangeComparators, @@ -120,7 +81,7 @@ export const getChangePointChartEmbeddableFactory = ( const blockingError = new BehaviorSubject(undefined); const dataViews$ = new BehaviorSubject([ - await deps.data.dataViews.get(state.dataViewId), + await pluginStart.data.dataViews.get(state.dataViewId), ]); const api = buildApi( diff --git a/x-pack/plugins/aiops/public/embeddables/change_point_chart/types.ts b/x-pack/plugins/aiops/public/embeddables/change_point_chart/types.ts index 4a39020a299c9..f86270bdaf19d 100644 --- a/x-pack/plugins/aiops/public/embeddables/change_point_chart/types.ts +++ b/x-pack/plugins/aiops/public/embeddables/change_point_chart/types.ts @@ -15,14 +15,6 @@ import type { SerializedTimeRange, SerializedTitles, } from '@kbn/presentation-publishing'; -import type { FC } from 'react'; -import type { SelectedChangePoint } from '../../components/change_point_detection/change_point_detection_context'; - -export type ViewComponent = FC<{ - changePoints: SelectedChangePoint[]; - interval: string; - onRenderComplete?: () => void; -}>; export interface ChangePointComponentApi { viewType: PublishingSubject; diff --git a/x-pack/plugins/aiops/public/embeddables/index.ts b/x-pack/plugins/aiops/public/embeddables/index.ts index b7d9ad25951fb..dae1f0eb3eeec 100644 --- a/x-pack/plugins/aiops/public/embeddables/index.ts +++ b/x-pack/plugins/aiops/public/embeddables/index.ts @@ -9,6 +9,7 @@ import type { CoreSetup } from '@kbn/core-lifecycle-browser'; import type { EmbeddableSetup } from '@kbn/embeddable-plugin/public'; import { EMBEDDABLE_CHANGE_POINT_CHART_TYPE } from '@kbn/aiops-change-point-detection/constants'; import { EMBEDDABLE_PATTERN_ANALYSIS_TYPE } from '@kbn/aiops-log-pattern-analysis/constants'; +import { EMBEDDABLE_LOG_RATE_ANALYSIS_TYPE } from '@kbn/aiops-log-rate-analysis/constants'; import type { AiopsPluginStart, AiopsPluginStartDeps } from '../types'; export const registerEmbeddables = ( @@ -23,4 +24,8 @@ export const registerEmbeddables = ( const { getPatternAnalysisEmbeddableFactory } = await import('./pattern_analysis'); return getPatternAnalysisEmbeddableFactory(core.getStartServices); }); + embeddable.registerReactEmbeddableFactory(EMBEDDABLE_LOG_RATE_ANALYSIS_TYPE, async () => { + const { getLogRateAnalysisEmbeddableFactory } = await import('./log_rate_analysis'); + return getLogRateAnalysisEmbeddableFactory(core.getStartServices); + }); }; diff --git a/x-pack/plugins/aiops/public/embeddables/log_rate_analysis/embeddable_log_rate_analysis_factory.tsx b/x-pack/plugins/aiops/public/embeddables/log_rate_analysis/embeddable_log_rate_analysis_factory.tsx new file mode 100644 index 0000000000000..592ec32cef120 --- /dev/null +++ b/x-pack/plugins/aiops/public/embeddables/log_rate_analysis/embeddable_log_rate_analysis_factory.tsx @@ -0,0 +1,211 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EMBEDDABLE_LOG_RATE_ANALYSIS_TYPE, + LOG_RATE_ANALYSIS_DATA_VIEW_REF_NAME, +} from '@kbn/aiops-log-rate-analysis/constants'; +import type { Reference } from '@kbn/content-management-utils'; +import type { StartServicesAccessor } from '@kbn/core-lifecycle-browser'; +import type { DataView } from '@kbn/data-views-plugin/common'; +import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-views-plugin/common'; +import type { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; +import { i18n } from '@kbn/i18n'; +import { + apiHasExecutionContext, + fetch$, + initializeTimeRange, + initializeTitles, + useBatchedPublishingSubjects, +} from '@kbn/presentation-publishing'; + +import fastIsEqual from 'fast-deep-equal'; +import { cloneDeep } from 'lodash'; +import React, { useMemo } from 'react'; +import useObservable from 'react-use/lib/useObservable'; +import { BehaviorSubject, distinctUntilChanged, map, skipWhile } from 'rxjs'; +import { getLogRateAnalysisEmbeddableWrapperComponent } from '../../shared_components'; +import type { AiopsPluginStart, AiopsPluginStartDeps } from '../../types'; +import { initializeLogRateAnalysisControls } from './initialize_log_rate_analysis_analysis_controls'; +import type { + LogRateAnalysisEmbeddableApi, + LogRateAnalysisEmbeddableRuntimeState, + LogRateAnalysisEmbeddableState, +} from './types'; + +export const getLogRateAnalysisEmbeddableFactory = ( + getStartServices: StartServicesAccessor +) => { + const factory: ReactEmbeddableFactory< + LogRateAnalysisEmbeddableState, + LogRateAnalysisEmbeddableRuntimeState, + LogRateAnalysisEmbeddableApi + > = { + type: EMBEDDABLE_LOG_RATE_ANALYSIS_TYPE, + deserializeState: (state) => { + const serializedState = cloneDeep(state.rawState); + // inject the reference + const dataViewIdRef = state.references?.find( + (ref) => ref.name === LOG_RATE_ANALYSIS_DATA_VIEW_REF_NAME + ); + // if the serializedState already contains a dataViewId, we don't want to overwrite it. (Unsaved state can cause this) + if (dataViewIdRef && serializedState && !serializedState.dataViewId) { + serializedState.dataViewId = dataViewIdRef?.id; + } + return serializedState; + }, + buildEmbeddable: async (state, buildApi, uuid, parentApi) => { + const [coreStart, pluginStart] = await getStartServices(); + + const { + api: timeRangeApi, + comparators: timeRangeComparators, + serialize: serializeTimeRange, + } = initializeTimeRange(state); + + const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + + const { + logRateAnalysisControlsApi, + serializeLogRateAnalysisChartState, + logRateAnalysisControlsComparators, + } = initializeLogRateAnalysisControls(state); + + const dataLoading = new BehaviorSubject(true); + const blockingError = new BehaviorSubject(undefined); + + const dataViews$ = new BehaviorSubject([ + await pluginStart.data.dataViews.get( + state.dataViewId ?? (await pluginStart.data.dataViews.getDefaultId()) + ), + ]); + + const api = buildApi( + { + ...timeRangeApi, + ...titlesApi, + ...logRateAnalysisControlsApi, + getTypeDisplayName: () => + i18n.translate('xpack.aiops.logRateAnalysis.typeDisplayName', { + defaultMessage: 'log rate analysis', + }), + isEditingEnabled: () => true, + onEdit: async () => { + try { + const { resolveEmbeddableLogRateAnalysisUserInput } = await import( + './resolve_log_rate_analysis_config_input' + ); + + const result = await resolveEmbeddableLogRateAnalysisUserInput( + coreStart, + pluginStart, + parentApi, + uuid, + false, + logRateAnalysisControlsApi, + undefined, + serializeLogRateAnalysisChartState() + ); + + logRateAnalysisControlsApi.updateUserInput(result); + } catch (e) { + return Promise.reject(); + } + }, + dataLoading, + blockingError, + dataViews: dataViews$, + serializeState: () => { + const dataViewId = logRateAnalysisControlsApi.dataViewId.getValue(); + const references: Reference[] = dataViewId + ? [ + { + type: DATA_VIEW_SAVED_OBJECT_TYPE, + name: LOG_RATE_ANALYSIS_DATA_VIEW_REF_NAME, + id: dataViewId, + }, + ] + : []; + return { + rawState: { + timeRange: undefined, + ...serializeTitles(), + ...serializeTimeRange(), + ...serializeLogRateAnalysisChartState(), + }, + references, + }; + }, + }, + { + ...timeRangeComparators, + ...titleComparators, + ...logRateAnalysisControlsComparators, + } + ); + + const LogRateAnalysisEmbeddableWrapper = getLogRateAnalysisEmbeddableWrapperComponent( + coreStart, + pluginStart + ); + + const onLoading = (v: boolean) => dataLoading.next(v); + const onRenderComplete = () => dataLoading.next(false); + const onError = (error: Error) => blockingError.next(error); + + return { + api, + Component: () => { + if (!apiHasExecutionContext(parentApi)) { + throw new Error('Parent API does not have execution context'); + } + + const [dataViewId] = useBatchedPublishingSubjects(api.dataViewId); + + const reload$ = useMemo( + () => + fetch$(api).pipe( + skipWhile((fetchContext) => !fetchContext.isReload), + map((fetchContext) => Date.now()) + ), + [] + ); + + const timeRange$ = useMemo( + () => + fetch$(api).pipe( + map((fetchContext) => fetchContext.timeRange), + distinctUntilChanged(fastIsEqual) + ), + [] + ); + + const lastReloadRequestTime = useObservable(reload$, Date.now()); + const timeRange = useObservable(timeRange$, undefined); + + const embeddingOrigin = apiHasExecutionContext(parentApi) + ? parentApi.executionContext.type + : undefined; + + return ( + + ); + }, + }; + }, + }; + + return factory; +}; diff --git a/x-pack/plugins/cloud/common/index.ts b/x-pack/plugins/aiops/public/embeddables/log_rate_analysis/index.ts similarity index 72% rename from x-pack/plugins/cloud/common/index.ts rename to x-pack/plugins/aiops/public/embeddables/log_rate_analysis/index.ts index 4aa6ce8d5edf0..2203d4c64bc8b 100644 --- a/x-pack/plugins/cloud/common/index.ts +++ b/x-pack/plugins/aiops/public/embeddables/log_rate_analysis/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export type { OnBoardingDefaultSolution } from './types'; +export { getLogRateAnalysisEmbeddableFactory } from './embeddable_log_rate_analysis_factory'; diff --git a/x-pack/plugins/aiops/public/embeddables/log_rate_analysis/initialize_log_rate_analysis_analysis_controls.ts b/x-pack/plugins/aiops/public/embeddables/log_rate_analysis/initialize_log_rate_analysis_analysis_controls.ts new file mode 100644 index 0000000000000..9d8a49b8f0e9c --- /dev/null +++ b/x-pack/plugins/aiops/public/embeddables/log_rate_analysis/initialize_log_rate_analysis_analysis_controls.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { StateComparators } from '@kbn/presentation-publishing'; +import { BehaviorSubject } from 'rxjs'; +import type { LogRateAnalysisComponentApi, LogRateAnalysisEmbeddableState } from './types'; + +type LogRateAnalysisEmbeddableCustomState = Omit< + LogRateAnalysisEmbeddableState, + 'timeRange' | 'title' | 'description' | 'hidePanelTitles' +>; + +export const initializeLogRateAnalysisControls = (rawState: LogRateAnalysisEmbeddableState) => { + const dataViewId = new BehaviorSubject(rawState.dataViewId); + + const updateUserInput = (update: LogRateAnalysisEmbeddableCustomState) => { + dataViewId.next(update.dataViewId); + }; + + const serializeLogRateAnalysisChartState = (): LogRateAnalysisEmbeddableCustomState => { + return { + dataViewId: dataViewId.getValue(), + }; + }; + + const logRateAnalysisControlsComparators: StateComparators = + { + dataViewId: [dataViewId, (arg) => dataViewId.next(arg)], + }; + + return { + logRateAnalysisControlsApi: { + dataViewId, + updateUserInput, + } as unknown as LogRateAnalysisComponentApi, + serializeLogRateAnalysisChartState, + logRateAnalysisControlsComparators, + }; +}; diff --git a/x-pack/plugins/aiops/public/embeddables/log_rate_analysis/log_rate_analysis_embeddable_initializer.tsx b/x-pack/plugins/aiops/public/embeddables/log_rate_analysis/log_rate_analysis_embeddable_initializer.tsx new file mode 100644 index 0000000000000..bf53f07677739 --- /dev/null +++ b/x-pack/plugins/aiops/public/embeddables/log_rate_analysis/log_rate_analysis_embeddable_initializer.tsx @@ -0,0 +1,230 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { FC } from 'react'; +import React, { useEffect, useMemo, useState, useCallback } from 'react'; +import { pick } from 'lodash'; +import useMountedState from 'react-use/lib/useMountedState'; + +import { + EuiFlyoutHeader, + EuiTitle, + EuiFlyoutBody, + EuiForm, + EuiFormRow, + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiFlyoutFooter, + EuiSpacer, +} from '@elastic/eui'; + +import type { IndexPatternSelectProps } from '@kbn/unified-search-plugin/public'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; + +import { TimeFieldWarning } from '../../components/time_field_warning'; + +import type { LogRateAnalysisEmbeddableRuntimeState } from './types'; + +export interface LogRateAnalysisEmbeddableInitializerProps { + dataViews: DataViewsPublicPluginStart; + IndexPatternSelect: React.ComponentType; + initialInput?: Partial; + onCreate: (props: LogRateAnalysisEmbeddableRuntimeState) => void; + onCancel: () => void; + onPreview: (update: LogRateAnalysisEmbeddableRuntimeState) => Promise; + isNewPanel: boolean; +} + +export const LogRateAnalysisEmbeddableInitializer: FC< + LogRateAnalysisEmbeddableInitializerProps +> = ({ + dataViews, + IndexPatternSelect, + initialInput, + onCreate, + onCancel, + onPreview, + isNewPanel, +}) => { + const isMounted = useMountedState(); + + const [formInput, setFormInput] = useState( + pick(initialInput ?? {}, ['dataViewId']) as LogRateAnalysisEmbeddableRuntimeState + ); + + // State to track if the selected data view is time based, undefined is used + // to track that the check is in progress. + const [isDataViewTimeBased, setIsDataViewTimeBased] = useState(); + + const isFormValid = useMemo( + () => + isPopulatedObject(formInput, ['dataViewId']) && + formInput.dataViewId !== '' && + isDataViewTimeBased === true, + [formInput, isDataViewTimeBased] + ); + + const updatedProps = useMemo(() => { + return { + ...formInput, + title: isPopulatedObject(formInput) + ? i18n.translate('xpack.aiops.embeddableLogRateAnalysis.attachmentTitle', { + defaultMessage: 'Log rate analysis', + }) + : '', + }; + }, [formInput]); + + useEffect( + function previewChanges() { + if (isFormValid) { + onPreview(updatedProps); + } + }, + [isFormValid, onPreview, updatedProps, isDataViewTimeBased] + ); + + const setDataViewId = useCallback( + (dataViewId: string | undefined) => { + setFormInput({ + ...formInput, + dataViewId: dataViewId ?? '', + }); + setIsDataViewTimeBased(undefined); + }, + [formInput] + ); + + useEffect( + function checkIsDataViewTimeBased() { + setIsDataViewTimeBased(undefined); + + const { dataViewId } = formInput; + + if (!dataViewId) { + return; + } + + dataViews + .get(dataViewId) + .then((dataView) => { + if (!isMounted()) { + return; + } + setIsDataViewTimeBased(dataView.isTimeBased()); + }) + .catch(() => { + setIsDataViewTimeBased(undefined); + }); + }, + [dataViews, formInput, isMounted] + ); + + return ( + <> + + +

+ {isNewPanel + ? i18n.translate('xpack.aiops.embeddableLogRateAnalysis.config.title.new', { + defaultMessage: 'Create log rate analysis', + }) + : i18n.translate('xpack.aiops.embeddableLogRateAnalysis.config.title.edit', { + defaultMessage: 'Edit log rate analysis', + })} +

+
+
+ + + + + <> + { + setDataViewId(newId ?? ''); + }} + data-test-subj="aiopsLogRateAnalysisEmbeddableDataViewSelector" + /> + {isDataViewTimeBased === false && ( + <> + + + + )} + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/aiops/public/embeddables/log_rate_analysis/resolve_log_rate_analysis_config_input.tsx b/x-pack/plugins/aiops/public/embeddables/log_rate_analysis/resolve_log_rate_analysis_config_input.tsx new file mode 100644 index 0000000000000..a066b5bc722f2 --- /dev/null +++ b/x-pack/plugins/aiops/public/embeddables/log_rate_analysis/resolve_log_rate_analysis_config_input.tsx @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { CoreStart } from '@kbn/core/public'; +import { tracksOverlays } from '@kbn/presentation-containers'; +import { toMountPoint } from '@kbn/react-kibana-mount'; +import React from 'react'; +import type { AiopsPluginStartDeps } from '../../types'; +import { LogRateAnalysisEmbeddableInitializer } from './log_rate_analysis_embeddable_initializer'; +import type { LogRateAnalysisComponentApi, LogRateAnalysisEmbeddableState } from './types'; + +export async function resolveEmbeddableLogRateAnalysisUserInput( + coreStart: CoreStart, + pluginStart: AiopsPluginStartDeps, + parentApi: unknown, + focusedPanelId: string, + isNewPanel: boolean, + logRateAnalysisControlsApi: LogRateAnalysisComponentApi, + deletePanel?: () => void, + initialState?: LogRateAnalysisEmbeddableState +): Promise { + const { overlays } = coreStart; + + const overlayTracker = tracksOverlays(parentApi) ? parentApi : undefined; + + let hasChanged = false; + return new Promise(async (resolve, reject) => { + try { + const cancelChanges = () => { + if (isNewPanel && deletePanel) { + deletePanel(); + } else if (hasChanged && logRateAnalysisControlsApi && initialState) { + // Reset to initialState in case user has changed the preview state + logRateAnalysisControlsApi.updateUserInput(initialState); + } + + flyoutSession.close(); + overlayTracker?.clearOverlays(); + }; + + const update = async (nextUpdate: LogRateAnalysisEmbeddableState) => { + resolve(nextUpdate); + flyoutSession.close(); + overlayTracker?.clearOverlays(); + }; + + const preview = async (nextUpdate: LogRateAnalysisEmbeddableState) => { + if (logRateAnalysisControlsApi) { + logRateAnalysisControlsApi.updateUserInput(nextUpdate); + hasChanged = true; + } + }; + + const flyoutSession = overlays.openFlyout( + toMountPoint( + , + coreStart + ), + { + ownFocus: true, + size: 's', + type: 'push', + paddingSize: 'm', + hideCloseButton: true, + 'data-test-subj': 'aiopsLogRateAnalysisEmbeddableInitializer', + 'aria-labelledby': 'logRateAnalysisConfig', + onClose: () => { + reject(); + flyoutSession.close(); + overlayTracker?.clearOverlays(); + }, + } + ); + + if (tracksOverlays(parentApi)) { + parentApi.openOverlay(flyoutSession, { focusedPanelId }); + } + } catch (error) { + reject(error); + } + }); +} diff --git a/x-pack/plugins/aiops/public/embeddables/log_rate_analysis/types.ts b/x-pack/plugins/aiops/public/embeddables/log_rate_analysis/types.ts new file mode 100644 index 0000000000000..d2255e6dacb87 --- /dev/null +++ b/x-pack/plugins/aiops/public/embeddables/log_rate_analysis/types.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 { DefaultEmbeddableApi } from '@kbn/embeddable-plugin/public'; +import type { + HasEditCapabilities, + PublishesDataViews, + PublishesTimeRange, + PublishingSubject, + SerializedTimeRange, + SerializedTitles, +} from '@kbn/presentation-publishing'; + +export interface LogRateAnalysisComponentApi { + dataViewId: PublishingSubject; + updateUserInput: (update: LogRateAnalysisEmbeddableState) => void; +} + +export type LogRateAnalysisEmbeddableApi = DefaultEmbeddableApi & + HasEditCapabilities & + PublishesDataViews & + PublishesTimeRange & + LogRateAnalysisComponentApi; + +export interface LogRateAnalysisEmbeddableState extends SerializedTitles, SerializedTimeRange { + dataViewId: string; +} + +export interface LogRateAnalysisEmbeddableInitialState + extends SerializedTitles, + SerializedTimeRange { + dataViewId?: string; +} + +export type LogRateAnalysisEmbeddableRuntimeState = LogRateAnalysisEmbeddableState; diff --git a/x-pack/plugins/aiops/public/embeddables/pattern_analysis/embeddable_pattern_analysis_factory.tsx b/x-pack/plugins/aiops/public/embeddables/pattern_analysis/embeddable_pattern_analysis_factory.tsx index e7b1d6da3be61..d84043ea5f637 100644 --- a/x-pack/plugins/aiops/public/embeddables/pattern_analysis/embeddable_pattern_analysis_factory.tsx +++ b/x-pack/plugins/aiops/public/embeddables/pattern_analysis/embeddable_pattern_analysis_factory.tsx @@ -11,7 +11,6 @@ import { } from '@kbn/aiops-log-pattern-analysis/constants'; import type { Reference } from '@kbn/content-management-utils'; import type { StartServicesAccessor } from '@kbn/core-lifecycle-browser'; -import { type DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/common'; import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-views-plugin/common'; import type { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; @@ -37,32 +36,6 @@ import type { PatternAnalysisEmbeddableState, } from './types'; -export interface EmbeddablePatternAnalysisStartServices { - data: DataPublicPluginStart; -} - -export type EmbeddablePatternAnalysisType = typeof EMBEDDABLE_PATTERN_ANALYSIS_TYPE; - -export const getDependencies = async ( - getStartServices: StartServicesAccessor -) => { - const [ - { http, uiSettings, notifications, ...startServices }, - { lens, data, usageCollection, fieldFormats }, - ] = await getStartServices(); - - return { - http, - uiSettings, - data, - notifications, - lens, - usageCollection, - fieldFormats, - ...startServices, - }; -}; - export const getPatternAnalysisEmbeddableFactory = ( getStartServices: StartServicesAccessor ) => { @@ -87,20 +60,6 @@ export const getPatternAnalysisEmbeddableFactory = ( buildEmbeddable: async (state, buildApi, uuid, parentApi) => { const [coreStart, pluginStart] = await getStartServices(); - const { http, uiSettings, notifications, ...startServices } = coreStart; - const { lens, data, usageCollection, fieldFormats } = pluginStart; - - const deps = { - http, - uiSettings, - data, - notifications, - lens, - usageCollection, - fieldFormats, - ...startServices, - }; - const { api: timeRangeApi, comparators: timeRangeComparators, @@ -119,8 +78,8 @@ export const getPatternAnalysisEmbeddableFactory = ( const blockingError = new BehaviorSubject(undefined); const dataViews$ = new BehaviorSubject([ - await deps.data.dataViews.get( - state.dataViewId ?? (await deps.data.dataViews.getDefaultId()) + await pluginStart.data.dataViews.get( + state.dataViewId ?? (await pluginStart.data.dataViews.getDefaultId()) ), ]); diff --git a/x-pack/plugins/aiops/public/embeddables/pattern_analysis/pattern_analysys_component_wrapper.tsx b/x-pack/plugins/aiops/public/embeddables/pattern_analysis/pattern_analysis_component_wrapper.tsx similarity index 100% rename from x-pack/plugins/aiops/public/embeddables/pattern_analysis/pattern_analysys_component_wrapper.tsx rename to x-pack/plugins/aiops/public/embeddables/pattern_analysis/pattern_analysis_component_wrapper.tsx diff --git a/x-pack/plugins/aiops/public/embeddables/pattern_analysis/pattern_analysis_initializer.tsx b/x-pack/plugins/aiops/public/embeddables/pattern_analysis/pattern_analysis_initializer.tsx index f44fff343fb50..ef185518638b8 100644 --- a/x-pack/plugins/aiops/public/embeddables/pattern_analysis/pattern_analysis_initializer.tsx +++ b/x-pack/plugins/aiops/public/embeddables/pattern_analysis/pattern_analysis_initializer.tsx @@ -33,6 +33,7 @@ import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { DataSourceContextProvider } from '../../hooks/use_data_source'; import type { PatternAnalysisEmbeddableRuntimeState } from './types'; import { PatternAnalysisSettings } from '../../components/log_categorization/log_categorization_for_embeddable/embeddable_menu'; +import { TimeFieldWarning } from '../../components/time_field_warning'; import { RandomSampler } from '../../components/log_categorization/sampling_menu'; import { DEFAULT_PROBABILITY, @@ -62,6 +63,7 @@ export const PatternAnalysisEmbeddableInitializer: FC { const { + data: { dataViews }, unifiedSearch: { ui: { IndexPatternSelect }, }, @@ -166,7 +168,7 @@ export const PatternAnalysisEmbeddableInitializer: FC - + { ); }; - -const TimeFieldWarning = () => { - return ( - <> - -

- {i18n.translate( - 'xpack.aiops.logCategorization.embeddableMenu.timeFieldWarning.title.description', - { - defaultMessage: 'Pattern analysis can only be run on data views with a time field.', - } - )} -

-
- - - ); -}; diff --git a/x-pack/plugins/aiops/public/embeddables/pattern_analysis/types.ts b/x-pack/plugins/aiops/public/embeddables/pattern_analysis/types.ts index f78934b9075f1..710e18823a2bb 100644 --- a/x-pack/plugins/aiops/public/embeddables/pattern_analysis/types.ts +++ b/x-pack/plugins/aiops/public/embeddables/pattern_analysis/types.ts @@ -14,18 +14,12 @@ import type { SerializedTimeRange, SerializedTitles, } from '@kbn/presentation-publishing'; -import type { FC } from 'react'; import type { MinimumTimeRangeOption } from '../../components/log_categorization/log_categorization_for_embeddable/minimum_time_range'; import type { RandomSamplerOption, RandomSamplerProbability, } from '../../components/log_categorization/sampling_menu/random_sampler'; -export type ViewComponent = FC<{ - interval: string; - onRenderComplete?: () => void; -}>; - export interface PatternAnalysisComponentApi { dataViewId: PublishingSubject; fieldName: PublishingSubject; diff --git a/x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts b/x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts index 03762a7ba70ba..c240ec90bc1df 100644 --- a/x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts +++ b/x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { createContext, type FC, type PropsWithChildren, useContext } from 'react'; +import { createContext, type FC, useContext } from 'react'; import type { ObservabilityAIAssistantPublicStart } from '@kbn/observability-ai-assistant-plugin/public'; import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; @@ -24,17 +24,12 @@ import type { ThemeServiceStart, } from '@kbn/core/public'; import type { LensPublicStart } from '@kbn/lens-plugin/public'; -import { type EuiComboBoxProps } from '@elastic/eui/src/components/combo_box/combo_box'; -import { type DataView } from '@kbn/data-views-plugin/common'; -import type { - FieldStatsProps, - FieldStatsServices, -} from '@kbn/unified-field-list/src/components/field_stats'; -import type { TimeRange as TimeRangeMs } from '@kbn/ml-date-picker'; import type { EmbeddableStart } from '@kbn/embeddable-plugin/public'; import type { CasesPublicStart } from '@kbn/cases-plugin/public'; import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import type { FieldStatsFlyoutProviderProps } from '@kbn/ml-field-stats-flyout/field_stats_flyout_provider'; +import type { UseFieldStatsTrigger } from '@kbn/ml-field-stats-flyout/use_field_stats_trigger'; /** * AIOps app context value to be provided via React context. @@ -98,7 +93,7 @@ export interface AiopsAppContextValue { /** * Used to create deep links to other plugins. */ - share: SharePluginStart; + share?: SharePluginStart; /** * Used to create lens embeddables. */ @@ -115,18 +110,8 @@ export interface AiopsAppContextValue { * Deps for unified fields stats. */ fieldStats?: { - useFieldStatsTrigger: () => { - renderOption: EuiComboBoxProps['renderOption']; - closeFlyout: () => void; - }; - FieldStatsFlyoutProvider: FC< - PropsWithChildren<{ - dataView: DataView; - fieldStatsServices: FieldStatsServices; - timeRangeMs?: TimeRangeMs; - dslQuery?: FieldStatsProps['dslQuery']; - }> - >; + useFieldStatsTrigger: UseFieldStatsTrigger; + FieldStatsFlyoutProvider: FC; }; embeddable?: EmbeddableStart; cases?: CasesPublicStart; diff --git a/x-pack/plugins/aiops/public/hooks/use_data_source.tsx b/x-pack/plugins/aiops/public/hooks/use_data_source.tsx index 081e10a34de65..ef574a348b928 100644 --- a/x-pack/plugins/aiops/public/hooks/use_data_source.tsx +++ b/x-pack/plugins/aiops/public/hooks/use_data_source.tsx @@ -8,10 +8,10 @@ import type { FC, PropsWithChildren } from 'react'; import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'; import type { DataView } from '@kbn/data-views-plugin/common'; +import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { SavedSearch } from '@kbn/saved-search-plugin/public'; import { EuiEmptyPrompt } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { useAiopsAppContext } from './use_aiops_app_context'; export const DataSourceContext = createContext({ get dataView(): never { @@ -30,6 +30,7 @@ export interface DataViewAndSavedSearch { } export interface DataSourceContextProviderProps { + dataViews: DataViewsPublicPluginStart; dataViewId?: string; savedSearchId?: string; /** Output resolves data view objects */ @@ -43,20 +44,14 @@ export interface DataSourceContextProviderProps { * @constructor */ export const DataSourceContextProvider: FC> = ({ + dataViews, dataViewId, - savedSearchId, children, onChange, }) => { const [value, setValue] = useState(); const [error, setError] = useState(); - const { - data: { dataViews }, - // uiSettings, - // savedSearch: savedSearchService, - } = useAiopsAppContext(); - /** * Resolve data view or saved search if exists. */ diff --git a/x-pack/plugins/aiops/public/shared_components/change_point_detection.tsx b/x-pack/plugins/aiops/public/shared_components/change_point_detection.tsx index 9afbd9e1c4c8d..53997219fd639 100644 --- a/x-pack/plugins/aiops/public/shared_components/change_point_detection.tsx +++ b/x-pack/plugins/aiops/public/shared_components/change_point_detection.tsx @@ -11,7 +11,6 @@ import type { CoreStart } from '@kbn/core-lifecycle-browser'; import { UI_SETTINGS } from '@kbn/data-service'; import type { TimeRange } from '@kbn/es-query'; import { DatePickerContextProvider } from '@kbn/ml-date-picker'; -import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { pick } from 'lodash'; import React, { useEffect, useMemo, useState, type FC } from 'react'; import type { Observable } from 'rxjs'; @@ -141,33 +140,34 @@ const ChangePointDetectionWrapper: FC = ({ width: 100%; `} > - - - - - - - - - - - - - - - + + + + + + + + + + + + +
); }; diff --git a/x-pack/plugins/aiops/public/shared_components/index.tsx b/x-pack/plugins/aiops/public/shared_components/index.tsx index 1c5b85c4b79b6..b347d3ee24cac 100644 --- a/x-pack/plugins/aiops/public/shared_components/index.tsx +++ b/x-pack/plugins/aiops/public/shared_components/index.tsx @@ -11,6 +11,7 @@ import type { CoreStart } from '@kbn/core-lifecycle-browser'; import type { AiopsPluginStartDeps } from '../types'; import type { ChangePointDetectionSharedComponent } from './change_point_detection'; import type { PatternAnalysisSharedComponent } from './pattern_analysis'; +import type { LogRateAnalysisEmbeddableWrapper } from './log_rate_analysis_embeddable_wrapper'; const ChangePointDetectionLazy = dynamic(async () => import('./change_point_detection')); @@ -37,3 +38,24 @@ export const getPatternAnalysisComponent = ( }; export type { PatternAnalysisSharedComponent } from './pattern_analysis'; + +const LogRateAnalysisEmbeddableWrapperLazy = dynamic( + async () => import('./log_rate_analysis_embeddable_wrapper') +); + +export const getLogRateAnalysisEmbeddableWrapperComponent = ( + coreStart: CoreStart, + pluginStart: AiopsPluginStartDeps +): LogRateAnalysisEmbeddableWrapper => { + return React.memo((props) => { + return ( + + ); + }); +}; + +export type { LogRateAnalysisEmbeddableWrapper } from './log_rate_analysis_embeddable_wrapper'; diff --git a/x-pack/plugins/aiops/public/shared_components/log_rate_analysis_embeddable_wrapper.tsx b/x-pack/plugins/aiops/public/shared_components/log_rate_analysis_embeddable_wrapper.tsx new file mode 100644 index 0000000000000..9f2a88e73461c --- /dev/null +++ b/x-pack/plugins/aiops/public/shared_components/log_rate_analysis_embeddable_wrapper.tsx @@ -0,0 +1,179 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { pick } from 'lodash'; +import React, { useEffect, useMemo, useState, type FC } from 'react'; +import usePrevious from 'react-use/lib/usePrevious'; +import type { Observable } from 'rxjs'; +import { BehaviorSubject, combineLatest, distinctUntilChanged, map } from 'rxjs'; +import { createBrowserHistory } from 'history'; + +import { UrlStateProvider } from '@kbn/ml-url-state'; +import { Router } from '@kbn/shared-ux-router'; +import { AIOPS_EMBEDDABLE_ORIGIN } from '@kbn/aiops-common/constants'; +import type { CoreStart } from '@kbn/core-lifecycle-browser'; +import { UI_SETTINGS } from '@kbn/data-service'; +import { LogRateAnalysisReduxProvider } from '@kbn/aiops-log-rate-analysis/state'; +import type { TimeRange } from '@kbn/es-query'; +import { DatePickerContextProvider } from '@kbn/ml-date-picker'; +import type { SignificantItem } from '@kbn/ml-agg-utils'; + +import { AiopsAppContext, type AiopsAppContextValue } from '../hooks/use_aiops_app_context'; +import { DataSourceContextProvider } from '../hooks/use_data_source'; +import { ReloadContextProvider } from '../hooks/use_reload'; +import { FilterQueryContextProvider } from '../hooks/use_filters_query'; +import type { AiopsPluginStartDeps } from '../types'; + +import { LogRateAnalysisForEmbeddable } from '../components/log_rate_analysis/log_rate_analysis_for_embeddable'; + +/** + * Only used to initialize internally + */ +export type LogRateAnalysisPropsWithDeps = LogRateAnalysisEmbeddableWrapperProps & { + coreStart: CoreStart; + pluginStart: AiopsPluginStartDeps; +}; + +export type LogRateAnalysisEmbeddableWrapper = FC; + +export interface LogRateAnalysisEmbeddableWrapperProps { + dataViewId: string; + timeRange: TimeRange; + /** + * Component to render if there are no significant items found + */ + emptyState?: React.ReactElement; + /** + * Outputs the most recent significant items + */ + onChange?: (significantItems: SignificantItem[]) => void; + /** + * Last reload request time, can be used for manual reload + */ + lastReloadRequestTime?: number; + /** Origin of the embeddable instance */ + embeddingOrigin?: string; + onLoading: (isLoading: boolean) => void; + onRenderComplete: () => void; + onError: (error: Error) => void; +} + +const LogRateAnalysisEmbeddableWrapperWithDeps: FC = ({ + // Component dependencies + coreStart, + pluginStart, + // Component props + dataViewId, + timeRange, + embeddingOrigin, + lastReloadRequestTime, +}) => { + const deps = useMemo(() => { + const { lens, data, usageCollection, fieldFormats, charts, share, storage, unifiedSearch } = + pluginStart; + + return { + data, + lens, + usageCollection, + fieldFormats, + charts, + share, + storage, + unifiedSearch, + ...coreStart, + }; + }, [coreStart, pluginStart]); + + const datePickerDeps = { + ...pick(deps, ['data', 'http', 'notifications', 'theme', 'uiSettings', 'i18n']), + uiSettingsKeys: UI_SETTINGS, + }; + + const aiopsAppContextValue = useMemo(() => { + return { + embeddingOrigin: embeddingOrigin ?? AIOPS_EMBEDDABLE_ORIGIN.DEFAULT, + ...deps, + }; + }, [deps, embeddingOrigin]); + + const [manualReload$] = useState>( + new BehaviorSubject(lastReloadRequestTime ?? Date.now()) + ); + + useEffect( + function updateManualReloadSubject() { + if (!lastReloadRequestTime) return; + manualReload$.next(lastReloadRequestTime); + }, + [lastReloadRequestTime, manualReload$] + ); + + const resultObservable$ = useMemo>(() => { + return combineLatest([manualReload$]).pipe( + map(([manualReload]) => Math.max(manualReload)), + distinctUntilChanged() + ); + }, [manualReload$]); + + const history = createBrowserHistory(); + + // We use the following pattern to track changes of dataViewId, and if there's + // a change, we unmount and remount the complete inner component. This makes + // sure the component is reinitialized correctly when the options of the + // dashboard panel are used to change the data view. This is a bit of a + // workaround since originally log rate analysis was developed as a standalone + // page with the expectation that the data view is set once and never changes. + const prevDataViewId = usePrevious(dataViewId); + const [_, setRerenderFlag] = useState(false); + useEffect(() => { + if (prevDataViewId && prevDataViewId !== dataViewId) { + setRerenderFlag((prev) => !prev); + } + }, [dataViewId, prevDataViewId]); + const showComponent = prevDataViewId === undefined || prevDataViewId === dataViewId; + + // TODO: Remove data-shared-item as part of https://github.com/elastic/kibana/issues/179376> + return ( +
+ {showComponent && ( + + + + + + + + + + + + + + + + + + )} +
+ ); +}; + +// eslint-disable-next-line import/no-default-export +export default LogRateAnalysisEmbeddableWrapperWithDeps; diff --git a/x-pack/plugins/aiops/public/shared_components/pattern_analysis.tsx b/x-pack/plugins/aiops/public/shared_components/pattern_analysis.tsx index 78261cd1f62f0..f601474a5707f 100644 --- a/x-pack/plugins/aiops/public/shared_components/pattern_analysis.tsx +++ b/x-pack/plugins/aiops/public/shared_components/pattern_analysis.tsx @@ -10,7 +10,6 @@ import type { CoreStart } from '@kbn/core-lifecycle-browser'; import { UI_SETTINGS } from '@kbn/data-service'; import type { TimeRange } from '@kbn/es-query'; import { DatePickerContextProvider } from '@kbn/ml-date-picker'; -import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { pick } from 'lodash'; import React, { useEffect, useMemo, useState, type FC } from 'react'; import type { Observable } from 'rxjs'; @@ -20,7 +19,7 @@ import type { RandomSamplerOption, RandomSamplerProbability, } from '../components/log_categorization/sampling_menu/random_sampler'; -import { PatternAnalysisEmbeddableWrapper } from '../embeddables/pattern_analysis/pattern_analysys_component_wrapper'; +import { PatternAnalysisEmbeddableWrapper } from '../embeddables/pattern_analysis/pattern_analysis_component_wrapper'; import { AiopsAppContext, type AiopsAppContextValue } from '../hooks/use_aiops_app_context'; import { DataSourceContextProvider } from '../hooks/use_data_source'; import { FilterQueryContextProvider } from '../hooks/use_filters_query'; @@ -139,31 +138,32 @@ const PatternAnalysisWrapper: FC = ({ padding: '10px', }} > - - - - - - - - - - - - - + + + + + + + + + + +
); }; diff --git a/x-pack/plugins/aiops/public/types/storage.ts b/x-pack/plugins/aiops/public/types/storage.ts index a4a29dda2f2c3..ea6fde6b06552 100644 --- a/x-pack/plugins/aiops/public/types/storage.ts +++ b/x-pack/plugins/aiops/public/types/storage.ts @@ -19,14 +19,12 @@ export const AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE = 'aiops.randomSamplingProbabilityPreference'; export const AIOPS_PATTERN_ANALYSIS_MINIMUM_TIME_RANGE_PREFERENCE = 'aiops.patternAnalysisMinimumTimeRangePreference'; -export const AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS = 'aiops.logRateAnalysisResultColumns'; export type AiOps = Partial<{ [AIOPS_FROZEN_TIER_PREFERENCE]: FrozenTierPreference; [AIOPS_RANDOM_SAMPLING_MODE_PREFERENCE]: RandomSamplerOption; [AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE]: number; [AIOPS_PATTERN_ANALYSIS_MINIMUM_TIME_RANGE_PREFERENCE]: MinimumTimeRangeOption; - [AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS]: string[]; }> | null; export type AiOpsKey = keyof Exclude; @@ -39,8 +37,6 @@ export type AiOpsStorageMapped = T extends typeof AIOPS_FROZ ? RandomSamplerProbability : T extends typeof AIOPS_PATTERN_ANALYSIS_MINIMUM_TIME_RANGE_PREFERENCE ? MinimumTimeRangeOption - : T extends typeof AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS - ? string[] : null; export const AIOPS_STORAGE_KEYS = [ @@ -48,5 +44,4 @@ export const AIOPS_STORAGE_KEYS = [ AIOPS_RANDOM_SAMPLING_MODE_PREFERENCE, AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE, AIOPS_PATTERN_ANALYSIS_MINIMUM_TIME_RANGE_PREFERENCE, - AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS, ] as const; diff --git a/x-pack/plugins/aiops/public/ui_actions/create_change_point_chart.tsx b/x-pack/plugins/aiops/public/ui_actions/create_change_point_chart.tsx index f9078f575818a..4d7ed26e295f5 100644 --- a/x-pack/plugins/aiops/public/ui_actions/create_change_point_chart.tsx +++ b/x-pack/plugins/aiops/public/ui_actions/create_change_point_chart.tsx @@ -12,7 +12,9 @@ import type { UiActionsActionDefinition } from '@kbn/ui-actions-plugin/public'; import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; import { EMBEDDABLE_CHANGE_POINT_CHART_TYPE } from '@kbn/aiops-change-point-detection/constants'; import type { CoreStart } from '@kbn/core-lifecycle-browser'; + import type { AiopsPluginStartDeps } from '../types'; + import type { ChangePointChartActionContext } from './change_point_action_context'; const parentApiIsCompatible = async ( diff --git a/x-pack/plugins/aiops/public/ui_actions/create_log_rate_analysis_actions.tsx b/x-pack/plugins/aiops/public/ui_actions/create_log_rate_analysis_actions.tsx new file mode 100644 index 0000000000000..588903e96aa16 --- /dev/null +++ b/x-pack/plugins/aiops/public/ui_actions/create_log_rate_analysis_actions.tsx @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { PresentationContainer } from '@kbn/presentation-containers'; +import type { EmbeddableApiContext } from '@kbn/presentation-publishing'; +import type { UiActionsActionDefinition } from '@kbn/ui-actions-plugin/public'; +import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; +import type { CoreStart } from '@kbn/core-lifecycle-browser'; +import { EMBEDDABLE_LOG_RATE_ANALYSIS_TYPE } from '@kbn/aiops-log-rate-analysis/constants'; +import { AIOPS_EMBEDDABLE_GROUPING } from '@kbn/aiops-common/constants'; + +import type { + LogRateAnalysisEmbeddableApi, + LogRateAnalysisEmbeddableInitialState, +} from '../embeddables/log_rate_analysis/types'; +import type { AiopsPluginStartDeps } from '../types'; + +import type { LogRateAnalysisActionContext } from './log_rate_analysis_action_context'; + +const parentApiIsCompatible = async ( + parentApi: unknown +): Promise => { + const { apiIsPresentationContainer } = await import('@kbn/presentation-containers'); + // we cannot have an async type check, so return the casted parentApi rather than a boolean + return apiIsPresentationContainer(parentApi) ? (parentApi as PresentationContainer) : undefined; +}; + +export function createAddLogRateAnalysisEmbeddableAction( + coreStart: CoreStart, + pluginStart: AiopsPluginStartDeps +): UiActionsActionDefinition { + return { + id: 'create-log-rate-analysis-embeddable', + grouping: AIOPS_EMBEDDABLE_GROUPING, + getIconType: () => 'logRateAnalysis', + getDisplayName: () => + i18n.translate('xpack.aiops.embeddableLogRateAnalysisDisplayName', { + defaultMessage: 'Log rate analysis', + }), + async isCompatible(context: EmbeddableApiContext) { + return Boolean(await parentApiIsCompatible(context.embeddable)); + }, + async execute(context) { + const presentationContainerParent = await parentApiIsCompatible(context.embeddable); + if (!presentationContainerParent) throw new IncompatibleActionError(); + + try { + const { resolveEmbeddableLogRateAnalysisUserInput } = await import( + '../embeddables/log_rate_analysis/resolve_log_rate_analysis_config_input' + ); + + const initialState: LogRateAnalysisEmbeddableInitialState = { + dataViewId: undefined, + }; + + const embeddable = await presentationContainerParent.addNewPanel< + object, + LogRateAnalysisEmbeddableApi + >({ + panelType: EMBEDDABLE_LOG_RATE_ANALYSIS_TYPE, + initialState, + }); + + if (!embeddable) { + return; + } + + const deletePanel = () => { + presentationContainerParent.removePanel(embeddable.uuid); + }; + + resolveEmbeddableLogRateAnalysisUserInput( + coreStart, + pluginStart, + context.embeddable, + embeddable.uuid, + true, + embeddable, + deletePanel + ); + } catch (e) { + return Promise.reject(); + } + }, + }; +} diff --git a/x-pack/plugins/aiops/public/ui_actions/create_pattern_analysis_action.tsx b/x-pack/plugins/aiops/public/ui_actions/create_pattern_analysis_action.tsx index 81127559e4e3d..f840e896abac4 100644 --- a/x-pack/plugins/aiops/public/ui_actions/create_pattern_analysis_action.tsx +++ b/x-pack/plugins/aiops/public/ui_actions/create_pattern_analysis_action.tsx @@ -12,13 +12,16 @@ import type { UiActionsActionDefinition } from '@kbn/ui-actions-plugin/public'; import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; import type { CoreStart } from '@kbn/core-lifecycle-browser'; import { EMBEDDABLE_PATTERN_ANALYSIS_TYPE } from '@kbn/aiops-log-pattern-analysis/constants'; +import { AIOPS_EMBEDDABLE_GROUPING } from '@kbn/aiops-common/constants'; + import type { AiopsPluginStartDeps } from '../types'; -import type { PatternAnalysisActionContext } from './pattern_analysis_action_context'; import type { PatternAnalysisEmbeddableApi, PatternAnalysisEmbeddableInitialState, } from '../embeddables/pattern_analysis/types'; +import type { PatternAnalysisActionContext } from './pattern_analysis_action_context'; + const parentApiIsCompatible = async ( parentApi: unknown ): Promise => { @@ -33,16 +36,7 @@ export function createAddPatternAnalysisEmbeddableAction( ): UiActionsActionDefinition { return { id: 'create-pattern-analysis-embeddable', - grouping: [ - { - id: 'ml', - getDisplayName: () => - i18n.translate('xpack.aiops.navMenu.mlAppNameText', { - defaultMessage: 'Machine Learning and Analytics', - }), - getIconType: () => 'logPatternAnalysis', - }, - ], + grouping: AIOPS_EMBEDDABLE_GROUPING, getIconType: () => 'logPatternAnalysis', getDisplayName: () => i18n.translate('xpack.aiops.embeddablePatternAnalysisDisplayName', { diff --git a/x-pack/plugins/aiops/public/ui_actions/index.ts b/x-pack/plugins/aiops/public/ui_actions/index.ts index d14856fd28733..6081541c448e7 100644 --- a/x-pack/plugins/aiops/public/ui_actions/index.ts +++ b/x-pack/plugins/aiops/public/ui_actions/index.ts @@ -18,6 +18,7 @@ import { createOpenChangePointInMlAppAction } from './open_change_point_ml'; import type { AiopsPluginStartDeps } from '../types'; import { createCategorizeFieldAction } from '../components/log_categorization'; import { createAddPatternAnalysisEmbeddableAction } from './create_pattern_analysis_action'; +import { createAddLogRateAnalysisEmbeddableAction } from './create_log_rate_analysis_actions'; export function registerAiopsUiActions( uiActions: UiActionsSetup, @@ -27,6 +28,7 @@ export function registerAiopsUiActions( const openChangePointInMlAppAction = createOpenChangePointInMlAppAction(coreStart, pluginStart); const addChangePointChartAction = createAddChangePointChartAction(coreStart, pluginStart); const addPatternAnalysisAction = createAddPatternAnalysisEmbeddableAction(coreStart, pluginStart); + const addLogRateAnalysisAction = createAddLogRateAnalysisEmbeddableAction(coreStart, pluginStart); uiActions.addTriggerAction(ADD_PANEL_TRIGGER, addPatternAnalysisAction); uiActions.addTriggerAction(ADD_PANEL_TRIGGER, addChangePointChartAction); @@ -39,4 +41,6 @@ export function registerAiopsUiActions( ); uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, openChangePointInMlAppAction); + + uiActions.addTriggerAction(ADD_PANEL_TRIGGER, addLogRateAnalysisAction); } diff --git a/x-pack/plugins/aiops/public/ui_actions/log_rate_analysis_action_context.ts b/x-pack/plugins/aiops/public/ui_actions/log_rate_analysis_action_context.ts new file mode 100644 index 0000000000000..d0c1ef715b84c --- /dev/null +++ b/x-pack/plugins/aiops/public/ui_actions/log_rate_analysis_action_context.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 { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { apiIsOfType, type EmbeddableApiContext } from '@kbn/presentation-publishing'; +import { EMBEDDABLE_LOG_RATE_ANALYSIS_TYPE } from '@kbn/aiops-log-rate-analysis/constants'; +import type { LogRateAnalysisEmbeddableApi } from '../embeddables/log_rate_analysis/types'; + +export interface LogRateAnalysisActionContext extends EmbeddableApiContext { + embeddable: LogRateAnalysisEmbeddableApi; +} + +export function isLogRateAnalysisEmbeddableContext( + arg: unknown +): arg is LogRateAnalysisActionContext { + return ( + isPopulatedObject(arg, ['embeddable']) && + apiIsOfType(arg.embeddable, EMBEDDABLE_LOG_RATE_ANALYSIS_TYPE) + ); +} diff --git a/x-pack/plugins/aiops/server/routes/categorization_field_validation/define_route.ts b/x-pack/plugins/aiops/server/routes/categorization_field_validation/define_route.ts index ecc4d9c94bde1..5d166d1493b33 100644 --- a/x-pack/plugins/aiops/server/routes/categorization_field_validation/define_route.ts +++ b/x-pack/plugins/aiops/server/routes/categorization_field_validation/define_route.ts @@ -27,6 +27,13 @@ export const defineRoute = ( .addVersion( { version: '1', + security: { + authz: { + enabled: false, + reason: + 'This route is opted out from authorization because permissions will be checked by elasticsearch', + }, + }, validate: { request: { body: categorizationFieldValidationSchema, diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/define_route.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/define_route.ts index 5c092c1a3be58..b69a66e4e69c5 100644 --- a/x-pack/plugins/aiops/server/routes/log_rate_analysis/define_route.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/define_route.ts @@ -38,6 +38,13 @@ export const defineRoute = ( .addVersion( { version: '2', + security: { + authz: { + enabled: false, + reason: + 'This route is opted out from authorization because permissions will be checked by elasticsearch', + }, + }, validate: { request: { body: aiopsLogRateAnalysisSchemaV2, @@ -49,6 +56,13 @@ export const defineRoute = ( .addVersion( { version: '3', + security: { + authz: { + enabled: false, + reason: + 'This route is opted out from authorization because permissions will be checked by elasticsearch', + }, + }, validate: { request: { body: aiopsLogRateAnalysisSchemaV3, diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis_field_candidates/define_route.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis_field_candidates/define_route.ts index 132ecfee7b212..fc7ed7c808975 100644 --- a/x-pack/plugins/aiops/server/routes/log_rate_analysis_field_candidates/define_route.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis_field_candidates/define_route.ts @@ -35,6 +35,13 @@ export const defineRoute = ( .addVersion( { version: '1', + security: { + authz: { + enabled: false, + reason: + 'This route is opted out from authorization because permissions will be checked by elasticsearch', + }, + }, validate: { request: { body: aiopsLogRateAnalysisSchemaV3, diff --git a/x-pack/plugins/aiops/tsconfig.json b/x-pack/plugins/aiops/tsconfig.json index 188f8b275fbac..e8b4f4f3ed972 100644 --- a/x-pack/plugins/aiops/tsconfig.json +++ b/x-pack/plugins/aiops/tsconfig.json @@ -63,7 +63,6 @@ "@kbn/presentation-containers", "@kbn/presentation-publishing", "@kbn/presentation-util-plugin", - "@kbn/react-kibana-context-render", "@kbn/react-kibana-context-theme", "@kbn/react-kibana-mount", "@kbn/rison", @@ -79,6 +78,8 @@ "@kbn/observability-ai-assistant-plugin", "@kbn/ui-theme", "@kbn/apm-utils", + "@kbn/ml-field-stats-flyout", + "@kbn/shared-ux-router", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/alerting/common/routes/maintenance_window/apis/find/index.ts b/x-pack/plugins/alerting/common/routes/maintenance_window/apis/find/index.ts index c3e30072e7348..8715e176f9935 100644 --- a/x-pack/plugins/alerting/common/routes/maintenance_window/apis/find/index.ts +++ b/x-pack/plugins/alerting/common/routes/maintenance_window/apis/find/index.ts @@ -5,6 +5,20 @@ * 2.0. */ -export type { FindMaintenanceWindowsResponse } from './types/latest'; +export { + findMaintenanceWindowsRequestQuerySchema, + findMaintenanceWindowsResponseBodySchema, +} from './schemas/latest'; +export type { + FindMaintenanceWindowsRequestQuery, + FindMaintenanceWindowsResponse, +} from './types/latest'; -export type { FindMaintenanceWindowsResponse as FindMaintenanceWindowsResponseV1 } from './types/v1'; +export { + findMaintenanceWindowsRequestQuerySchema as findMaintenanceWindowsRequestQuerySchemaV1, + findMaintenanceWindowsResponseBodySchema as findMaintenanceWindowsResponseBodySchemaV1, +} from './schemas/v1'; +export type { + FindMaintenanceWindowsRequestQuery as FindMaintenanceWindowsRequestQueryV1, + FindMaintenanceWindowsResponse as FindMaintenanceWindowsResponseV1, +} from './types/v1'; diff --git a/x-pack/packages/security-solution/common/src/cells/renderers/index.ts b/x-pack/plugins/alerting/common/routes/maintenance_window/apis/find/schemas/latest.ts similarity index 91% rename from x-pack/packages/security-solution/common/src/cells/renderers/index.ts rename to x-pack/plugins/alerting/common/routes/maintenance_window/apis/find/schemas/latest.ts index e42333a710c04..25300c97a6d2e 100644 --- a/x-pack/packages/security-solution/common/src/cells/renderers/index.ts +++ b/x-pack/plugins/alerting/common/routes/maintenance_window/apis/find/schemas/latest.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './get'; +export * from './v1'; diff --git a/x-pack/plugins/alerting/common/routes/maintenance_window/apis/find/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/maintenance_window/apis/find/schemas/v1.ts new file mode 100644 index 0000000000000..7c4dffdd1d94c --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/maintenance_window/apis/find/schemas/v1.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 { schema } from '@kbn/config-schema'; +import { maintenanceWindowResponseSchemaV1 } from '../../../response'; + +const MAX_DOCS = 10000; + +export const findMaintenanceWindowsRequestQuerySchema = schema.object( + { + // we do not need to use schema.maybe here, because if we do not pass property page, defaultValue will be used + page: schema.number({ + defaultValue: 1, + min: 1, + max: MAX_DOCS, + meta: { + description: 'The page number to return.', + }, + }), + // we do not need to use schema.maybe here, because if we do not pass property per_page, defaultValue will be used + per_page: schema.number({ + defaultValue: 1000, + min: 0, + max: 100, + meta: { + description: 'The number of maintenance windows to return per page.', + }, + }), + }, + { + validate: (params) => { + const pageAsNumber = params.page ?? 0; + const perPageAsNumber = params.per_page ?? 0; + + if (Math.max(pageAsNumber, pageAsNumber * perPageAsNumber) > MAX_DOCS) { + return `The number of documents is too high. Paginating through more than ${MAX_DOCS} documents is not possible.`; + } + }, + } +); + +export const findMaintenanceWindowsResponseBodySchema = schema.object({ + page: schema.number(), + per_page: schema.number(), + total: schema.number(), + data: schema.arrayOf(maintenanceWindowResponseSchemaV1), +}); diff --git a/x-pack/plugins/alerting/common/routes/maintenance_window/apis/find/types/latest.ts b/x-pack/plugins/alerting/common/routes/maintenance_window/apis/find/types/latest.ts index 4741df5c6c6c1..25300c97a6d2e 100644 --- a/x-pack/plugins/alerting/common/routes/maintenance_window/apis/find/types/latest.ts +++ b/x-pack/plugins/alerting/common/routes/maintenance_window/apis/find/types/latest.ts @@ -5,4 +5,4 @@ * 2.0. */ -export type { FindMaintenanceWindowsResponse } from './v1'; +export * from './v1'; diff --git a/x-pack/plugins/alerting/common/routes/maintenance_window/apis/find/types/v1.ts b/x-pack/plugins/alerting/common/routes/maintenance_window/apis/find/types/v1.ts index 0bdff90d3419f..0176d2e6689f8 100644 --- a/x-pack/plugins/alerting/common/routes/maintenance_window/apis/find/types/v1.ts +++ b/x-pack/plugins/alerting/common/routes/maintenance_window/apis/find/types/v1.ts @@ -5,11 +5,15 @@ * 2.0. */ -import { MaintenanceWindowResponseV1 } from '../../../response'; +import { TypeOf } from '@kbn/config-schema'; +import { + findMaintenanceWindowsResponseBodySchema, + findMaintenanceWindowsRequestQuerySchema, +} from '..'; -export interface FindMaintenanceWindowsResponse { - body: { - data: MaintenanceWindowResponseV1[]; - total: number; - }; -} +export type FindMaintenanceWindowsResponse = TypeOf< + typeof findMaintenanceWindowsResponseBodySchema +>; +export type FindMaintenanceWindowsRequestQuery = TypeOf< + typeof findMaintenanceWindowsRequestQuerySchema +>; 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 648b2b806978f..a9237e6be4ecc 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 @@ -6,7 +6,7 @@ */ import { schema } from '@kbn/config-schema'; -import { maintenanceWindowStatusV1 } from '..'; +import { maintenanceWindowStatus as maintenanceWindowStatusV1 } from '../constants/v1'; import { maintenanceWindowCategoryIdsSchemaV1 } from '../../shared'; import { rRuleResponseSchemaV1 } from '../../../r_rule'; import { alertsFilterQuerySchemaV1 } from '../../../alerts_filter_query'; diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/find.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/find.ts index c63e491198ce9..822fb6e2bae1f 100644 --- a/x-pack/plugins/alerting/public/services/maintenance_windows_api/find.ts +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/find.ts @@ -16,7 +16,7 @@ export async function findMaintenanceWindows({ }: { http: HttpSetup; }): Promise { - const res = await http.get( + const res = await http.get( `${INTERNAL_BASE_ALERTING_API_PATH}/rules/maintenance_window/_find` ); return res.data.map((mw) => transformMaintenanceWindowResponse(mw)); diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/find_maintenance_windows.test.ts b/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/find_maintenance_windows.test.ts index 8d06b335892b9..35c15ebb57c61 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/find_maintenance_windows.test.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/find_maintenance_windows.test.ts @@ -17,6 +17,7 @@ import { MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, } from '../../../../../common'; import { getMockMaintenanceWindow } from '../../../../data/maintenance_window/test_helpers'; +import { findMaintenanceWindowsParamsSchema } from './schemas'; const savedObjectsClient = savedObjectsClientMock.create(); const uiSettings = uiSettingsServiceMock.createClient(); @@ -37,8 +38,47 @@ describe('MaintenanceWindowClient - find', () => { jest.useRealTimers(); }); + it('throws an error if page is string', async () => { + savedObjectsClient.find.mockResolvedValueOnce({ + saved_objects: [ + { + attributes: getMockMaintenanceWindow({ expirationDate: new Date().toISOString() }), + id: 'test-1', + }, + { + attributes: getMockMaintenanceWindow({ expirationDate: new Date().toISOString() }), + id: 'test-2', + }, + ], + page: 1, + per_page: 5, + } as unknown as SavedObjectsFindResponse); + + await expect( + // @ts-expect-error: testing validation of strings + findMaintenanceWindows(mockContext, { page: 'dfsd', perPage: 10 }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + '"Error validating find maintenance windows data - [page]: expected value of type [number] but got [string]"' + ); + }); + + it('throws an error if savedObjectsClient.find will throw an error', async () => { + jest.useFakeTimers().setSystemTime(new Date('2023-02-26T00:00:00.000Z')); + + savedObjectsClient.find.mockImplementation(() => { + throw new Error('something went wrong!'); + }); + + await expect( + findMaintenanceWindows(mockContext, { page: 1, perPage: 10 }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + '"Failed to find maintenance window, Error: Error: something went wrong!: something went wrong!"' + ); + }); + it('should find maintenance windows', async () => { jest.useFakeTimers().setSystemTime(new Date('2023-02-26T00:00:00.000Z')); + const spy = jest.spyOn(findMaintenanceWindowsParamsSchema, 'validate'); savedObjectsClient.find.mockResolvedValueOnce({ saved_objects: [ @@ -51,10 +91,13 @@ describe('MaintenanceWindowClient - find', () => { id: 'test-2', }, ], + page: 1, + per_page: 5, } as unknown as SavedObjectsFindResponse); - const result = await findMaintenanceWindows(mockContext); + const result = await findMaintenanceWindows(mockContext, {}); + expect(spy).toHaveBeenCalledWith({}); expect(savedObjectsClient.find).toHaveBeenLastCalledWith({ type: MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, }); @@ -62,5 +105,7 @@ describe('MaintenanceWindowClient - find', () => { expect(result.data.length).toEqual(2); expect(result.data[0].id).toEqual('test-1'); expect(result.data[1].id).toEqual('test-2'); + expect(result.page).toEqual(1); + expect(result.perPage).toEqual(5); }); }); diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/find_maintenance_windows.ts b/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/find_maintenance_windows.ts index fe0f279ea4073..5cb1e01c1f1a0 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/find_maintenance_windows.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/find_maintenance_windows.ts @@ -9,17 +9,35 @@ import Boom from '@hapi/boom'; import { MaintenanceWindowClientContext } from '../../../../../common'; import { transformMaintenanceWindowAttributesToMaintenanceWindow } from '../../transforms'; import { findMaintenanceWindowSo } from '../../../../data/maintenance_window'; -import type { FindMaintenanceWindowsResult } from './types'; +import type { FindMaintenanceWindowsResult, FindMaintenanceWindowsParams } from './types'; +import { findMaintenanceWindowsParamsSchema } from './schemas'; export async function findMaintenanceWindows( - context: MaintenanceWindowClientContext + context: MaintenanceWindowClientContext, + params?: FindMaintenanceWindowsParams ): Promise { const { savedObjectsClient, logger } = context; try { - const result = await findMaintenanceWindowSo({ savedObjectsClient }); + if (params) { + findMaintenanceWindowsParamsSchema.validate(params); + } + } catch (error) { + throw Boom.badRequest(`Error validating find maintenance windows data - ${error.message}`); + } + + try { + const result = await findMaintenanceWindowSo({ + savedObjectsClient, + ...(params + ? { savedObjectsFindOptions: { page: params.page, perPage: params.perPage } } + : {}), + }); return { + page: result.page, + perPage: result.per_page, + total: result.total, data: result.saved_objects.map((so) => transformMaintenanceWindowAttributesToMaintenanceWindow({ attributes: so.attributes, diff --git a/x-pack/packages/security-solution/common/index.ts b/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/schemas/find_maintenance_window_params_schema.ts similarity index 56% rename from x-pack/packages/security-solution/common/index.ts rename to x-pack/plugins/alerting/server/application/maintenance_window/methods/find/schemas/find_maintenance_window_params_schema.ts index ba5d797648f13..e874882450c26 100644 --- a/x-pack/packages/security-solution/common/index.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/schemas/find_maintenance_window_params_schema.ts @@ -5,7 +5,9 @@ * 2.0. */ -export { HostDetailsButton } from './src/cells/renderers/host'; +import { schema } from '@kbn/config-schema'; -export * from './src/flyout'; -export * from './src/cells/renderers'; +export const findMaintenanceWindowsParamsSchema = schema.object({ + perPage: schema.maybe(schema.number()), + page: schema.maybe(schema.number()), +}); diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/schemas/find_maintenance_windows_result_schema.ts b/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/schemas/find_maintenance_windows_result_schema.ts index 1bdc2f00219ae..49b03325faa33 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/schemas/find_maintenance_windows_result_schema.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/schemas/find_maintenance_windows_result_schema.ts @@ -9,5 +9,8 @@ import { schema } from '@kbn/config-schema'; import { maintenanceWindowSchema } from '../../../schemas'; export const findMaintenanceWindowsResultSchema = schema.object({ + page: schema.number(), + perPage: schema.number(), data: schema.arrayOf(maintenanceWindowSchema), + total: schema.number(), }); diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/schemas/index.ts b/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/schemas/index.ts index 4b2f087c95505..4e6c55b08955f 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/schemas/index.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/schemas/index.ts @@ -6,3 +6,4 @@ */ export { findMaintenanceWindowsResultSchema } from './find_maintenance_windows_result_schema'; +export { findMaintenanceWindowsParamsSchema } from './find_maintenance_window_params_schema'; diff --git a/x-pack/packages/security-solution/common/src/cells/renderers/host/index.tsx b/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/types/find_maintenance_window_params.ts similarity index 55% rename from x-pack/packages/security-solution/common/src/cells/renderers/host/index.tsx rename to x-pack/plugins/alerting/server/application/maintenance_window/methods/find/types/find_maintenance_window_params.ts index a39397b233d60..878d5168c7e55 100644 --- a/x-pack/packages/security-solution/common/src/cells/renderers/host/index.tsx +++ b/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/types/find_maintenance_window_params.ts @@ -5,5 +5,7 @@ * 2.0. */ -export * from './button'; -export * from './with_expandable_flyout'; +import { TypeOf } from '@kbn/config-schema'; +import { findMaintenanceWindowsParamsSchema } from '../schemas'; + +export type FindMaintenanceWindowsParams = TypeOf; diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/types/index.ts b/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/types/index.ts index a5f00973bb82e..97472fc231ab6 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/types/index.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/methods/find/types/index.ts @@ -6,3 +6,4 @@ */ export type { FindMaintenanceWindowsResult } from './find_maintenance_window_result'; +export type { FindMaintenanceWindowsParams } from './find_maintenance_window_params'; diff --git a/x-pack/plugins/alerting/server/data/maintenance_window/methods/find_maintenance_window_so.ts b/x-pack/plugins/alerting/server/data/maintenance_window/methods/find_maintenance_window_so.ts index baaed546c88cb..d08a3c360cbb0 100644 --- a/x-pack/plugins/alerting/server/data/maintenance_window/methods/find_maintenance_window_so.ts +++ b/x-pack/plugins/alerting/server/data/maintenance_window/methods/find_maintenance_window_so.ts @@ -24,7 +24,7 @@ export const findMaintenanceWindowSo = ({ - ...savedObjectsFindOptions, + ...(savedObjectsFindOptions ? savedObjectsFindOptions : {}), type: MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, }); }; diff --git a/x-pack/plugins/alerting/server/index.ts b/x-pack/plugins/alerting/server/index.ts index 2f5cd3994e436..2cb2c212a91ba 100644 --- a/x-pack/plugins/alerting/server/index.ts +++ b/x-pack/plugins/alerting/server/index.ts @@ -84,20 +84,6 @@ export const config: PluginConfigDescriptor = { rules: { run: { alerts: { max: true } } }, }, deprecations: ({ renameFromRoot, deprecate }) => [ - renameFromRoot('xpack.alerts.healthCheck', 'xpack.alerting.healthCheck', { level: 'warning' }), - renameFromRoot( - 'xpack.alerts.invalidateApiKeysTask.interval', - 'xpack.alerting.invalidateApiKeysTask.interval', - { level: 'warning' } - ), - renameFromRoot( - 'xpack.alerts.invalidateApiKeysTask.removalDelay', - 'xpack.alerting.invalidateApiKeysTask.removalDelay', - { level: 'warning' } - ), - renameFromRoot('xpack.alerting.defaultRuleTaskTimeout', 'xpack.alerting.rules.run.timeout', { - level: 'warning', - }), deprecate('maxEphemeralActionsPerAlert', 'a future version', { level: 'warning', message: `Configuring "xpack.alerting.maxEphemeralActionsPerAlert" is deprecated and will be removed in a future version. Remove this setting to increase action execution resiliency.`, diff --git a/x-pack/plugins/alerting/server/integration_tests/__snapshots__/alert_as_data_fields.test.ts.snap b/x-pack/plugins/alerting/server/integration_tests/__snapshots__/alert_as_data_fields.test.ts.snap index 8c65843f2d844..0513842a6126b 100644 --- a/x-pack/plugins/alerting/server/integration_tests/__snapshots__/alert_as_data_fields.test.ts.snap +++ b/x-pack/plugins/alerting/server/integration_tests/__snapshots__/alert_as_data_fields.test.ts.snap @@ -9851,6 +9851,10 @@ Object { "required": false, "type": "text", }, + "error.stack_trace": Object { + "required": false, + "type": "wildcard", + }, "host.name": Object { "required": false, "type": "keyword", @@ -9991,6 +9995,10 @@ Object { "required": false, "type": "text", }, + "error.stack_trace": Object { + "required": false, + "type": "wildcard", + }, "host.name": Object { "required": false, "type": "keyword", @@ -10131,6 +10139,10 @@ Object { "required": false, "type": "text", }, + "error.stack_trace": Object { + "required": false, + "type": "wildcard", + }, "host.name": Object { "required": false, "type": "keyword", @@ -10271,6 +10283,10 @@ Object { "required": false, "type": "text", }, + "error.stack_trace": Object { + "required": false, + "type": "wildcard", + }, "host.name": Object { "required": false, "type": "keyword", @@ -10417,6 +10433,10 @@ Object { "required": false, "type": "text", }, + "error.stack_trace": Object { + "required": false, + "type": "wildcard", + }, "host.name": Object { "required": false, "type": "keyword", diff --git a/x-pack/plugins/alerting/server/maintenance_window_client/maintenance_window_client.ts b/x-pack/plugins/alerting/server/maintenance_window_client/maintenance_window_client.ts index 2c5e6f417ff3d..dba7ae0800ede 100644 --- a/x-pack/plugins/alerting/server/maintenance_window_client/maintenance_window_client.ts +++ b/x-pack/plugins/alerting/server/maintenance_window_client/maintenance_window_client.ts @@ -13,7 +13,10 @@ import type { GetMaintenanceWindowParams } from '../application/maintenance_wind import { updateMaintenanceWindow } from '../application/maintenance_window/methods/update/update_maintenance_window'; import type { UpdateMaintenanceWindowParams } from '../application/maintenance_window/methods/update/types'; import { findMaintenanceWindows } from '../application/maintenance_window/methods/find/find_maintenance_windows'; -import type { FindMaintenanceWindowsResult } from '../application/maintenance_window/methods/find/types'; +import type { + FindMaintenanceWindowsResult, + FindMaintenanceWindowsParams, +} from '../application/maintenance_window/methods/find/types'; import { deleteMaintenanceWindow } from '../application/maintenance_window/methods/delete/delete_maintenance_window'; import type { DeleteMaintenanceWindowParams } from '../application/maintenance_window/methods/delete/types'; import { archiveMaintenanceWindow } from '../application/maintenance_window/methods/archive/archive_maintenance_window'; @@ -75,7 +78,8 @@ export class MaintenanceWindowClient { getMaintenanceWindow(this.context, params); public update = (params: UpdateMaintenanceWindowParams): Promise => updateMaintenanceWindow(this.context, params); - public find = (): Promise => findMaintenanceWindows(this.context); + public find = (params?: FindMaintenanceWindowsParams): Promise => + findMaintenanceWindows(this.context, params); public delete = (params: DeleteMaintenanceWindowParams): Promise<{}> => deleteMaintenanceWindow(this.context, params); public archive = (params: ArchiveMaintenanceWindowParams): Promise => diff --git a/x-pack/plugins/alerting/server/manual_tests/action_param_templates.sh b/x-pack/plugins/alerting/server/manual_tests/action_param_templates.sh index 0b72c5e57f5d7..5a63a4a1ecf51 100644 --- a/x-pack/plugins/alerting/server/manual_tests/action_param_templates.sh +++ b/x-pack/plugins/alerting/server/manual_tests/action_param_templates.sh @@ -24,10 +24,10 @@ KIBANA_URL=https://elastic:changeme@localhost:5601 # create email action ACTION_ID_EMAIL=`curl -X POST --insecure --silent \ - $KIBANA_URL/api/actions/action \ + $KIBANA_URL/api/actions/connector \ -H "kbn-xsrf: foo" -H "content-type: application/json" \ -d '{ - "actionTypeId": ".email", + "connector_type_id": ".email", "name": "email for action_param_templates test", "config": { "from": "team-alerting@example.com", @@ -41,10 +41,10 @@ echo "email action id: $ACTION_ID_EMAIL" # create slack action ACTION_ID_SLACK=`curl -X POST --insecure --silent \ - $KIBANA_URL/api/actions/action \ + $KIBANA_URL/api/actions/connector \ -H "kbn-xsrf: foo" -H "content-type: application/json" \ -d "{ - \"actionTypeId\": \".slack\", + \"connector_type_id\": \".slack\", \"name\": \"slack for action_param_templates test\", \"config\": { }, @@ -56,10 +56,10 @@ echo "slack action id: $ACTION_ID_SLACK" # create webhook action ACTION_ID_WEBHOOK=`curl -X POST --insecure --silent \ - $KIBANA_URL/api/actions/action \ + $KIBANA_URL/api/actions/connector \ -H "kbn-xsrf: foo" -H "content-type: application/json" \ -d "{ - \"actionTypeId\": \".webhook\", + \"connector_type_id\": \".webhook\", \"name\": \"webhook for action_param_templates test\", \"config\": { \"url\": \"$SLACK_WEBHOOKURL\", @@ -108,7 +108,7 @@ ALERT_ID=`curl -X POST --insecure --silent \ } ], \"params\": { - \"index\": [\".kibana\"], + \"index\": [\".kibana\"], \"timeField\": \"updated_at\", \"aggType\": \"count\", \"groupBy\": \"all\", diff --git a/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/find_maintenance_windows_route.test.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/find_maintenance_windows_route.test.ts index 0a51513f4a08a..99c1cf7b23f6a 100644 --- a/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/find_maintenance_windows_route.test.ts +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/find_maintenance_windows_route.test.ts @@ -22,6 +22,9 @@ jest.mock('../../../../lib/license_api_access', () => ({ })); const mockMaintenanceWindows = { + page: 1, + perPage: 3, + total: 2, data: [ { ...getMockMaintenanceWindow(), @@ -67,11 +70,54 @@ describe('findMaintenanceWindowsRoute', () => { await handler(context, req, res); - expect(maintenanceWindowClient.find).toHaveBeenCalled(); + expect(maintenanceWindowClient.find).toHaveBeenCalledWith({}); expect(res.ok).toHaveBeenLastCalledWith({ body: { data: mockMaintenanceWindows.data.map((data) => rewriteMaintenanceWindowRes(data)), total: 2, + page: 1, + per_page: 3, + }, + }); + }); + + test('should find the maintenance windows with query', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + findMaintenanceWindowsRoute(router, licenseState); + + maintenanceWindowClient.find.mockResolvedValueOnce(mockMaintenanceWindows); + const [config, handler] = router.get.mock.calls[0]; + const [context, req, res] = mockHandlerArguments( + { maintenanceWindowClient }, + { + query: { + page: 1, + per_page: 3, + }, + } + ); + + expect(config.path).toEqual('/internal/alerting/rules/maintenance_window/_find'); + expect(config.options).toMatchInlineSnapshot(` + Object { + "access": "internal", + "tags": Array [ + "access:read-maintenance-window", + ], + } + `); + + await handler(context, req, res); + + expect(maintenanceWindowClient.find).toHaveBeenCalledWith({ page: 1, perPage: 3 }); + expect(res.ok).toHaveBeenLastCalledWith({ + body: { + data: mockMaintenanceWindows.data.map((data) => rewriteMaintenanceWindowRes(data)), + total: 2, + page: 1, + per_page: 3, }, }); }); diff --git a/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/find_maintenance_windows_route.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/find_maintenance_windows_route.ts index 7a7fb13160252..1aa4653e3d8d3 100644 --- a/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/find_maintenance_windows_route.ts +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/find_maintenance_windows_route.ts @@ -15,7 +15,15 @@ import { import { MAINTENANCE_WINDOW_API_PRIVILEGES } from '../../../../../common'; import type { FindMaintenanceWindowsResult } from '../../../../application/maintenance_window/methods/find/types'; import type { FindMaintenanceWindowsResponseV1 } from '../../../../../common/routes/maintenance_window/apis/find'; -import { transformMaintenanceWindowToResponseV1 } from '../../transforms'; +import { + findMaintenanceWindowsRequestQuerySchemaV1, + findMaintenanceWindowsResponseBodySchemaV1, + type FindMaintenanceWindowsRequestQueryV1, +} from '../../../../../common/routes/maintenance_window/apis/find'; +import { + transformFindMaintenanceWindowParamsV1, + transformFindMaintenanceWindowResponseV1, +} from './transforms'; export const findMaintenanceWindowsRoute = ( router: IRouter, @@ -24,7 +32,23 @@ export const findMaintenanceWindowsRoute = ( router.get( { path: `${INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH}/_find`, - validate: {}, + validate: { + request: { + query: findMaintenanceWindowsRequestQuerySchemaV1, + }, + response: { + 200: { + body: () => findMaintenanceWindowsResponseBodySchemaV1, + description: 'Indicates a successful call.', + }, + 400: { + description: 'Indicates an invalid schema or parameters.', + }, + 403: { + description: 'Indicates that this call is forbidden.', + }, + }, + }, options: { access: 'internal', tags: [`access:${MAINTENANCE_WINDOW_API_PRIVILEGES.READ_MAINTENANCE_WINDOW}`], @@ -34,20 +58,17 @@ export const findMaintenanceWindowsRoute = ( verifyAccessAndContext(licenseState, async function (context, req, res) { licenseState.ensureLicenseForMaintenanceWindow(); + const query: FindMaintenanceWindowsRequestQueryV1 = req.query || {}; const maintenanceWindowClient = (await context.alerting).getMaintenanceWindowClient(); - const result: FindMaintenanceWindowsResult = await maintenanceWindowClient.find(); - - const response: FindMaintenanceWindowsResponseV1 = { - body: { - data: result.data.map((maintenanceWindow) => - transformMaintenanceWindowToResponseV1(maintenanceWindow) - ), - total: result.data.length, - }, - }; + const options = transformFindMaintenanceWindowParamsV1(query); + const findResult: FindMaintenanceWindowsResult = await maintenanceWindowClient.find( + options + ); + const responseBody: FindMaintenanceWindowsResponseV1 = + transformFindMaintenanceWindowResponseV1(findResult); - return res.ok(response); + return res.ok({ body: responseBody }); }) ) ); diff --git a/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/transforms/index.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/transforms/index.ts new file mode 100644 index 0000000000000..43d428f9dd47a --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/transforms/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { transformFindMaintenanceWindowParams } from './transform_find_maintenance_window_params/latest'; +export { transformFindMaintenanceWindowResponse } from './transform_find_maintenance_window_to_response/latest'; + +export { transformFindMaintenanceWindowParams as transformFindMaintenanceWindowParamsV1 } from './transform_find_maintenance_window_params/v1'; +export { transformFindMaintenanceWindowResponse as transformFindMaintenanceWindowResponseV1 } from './transform_find_maintenance_window_to_response/v1'; diff --git a/x-pack/packages/security-solution/common/src/flyout/panels/keys.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/transforms/transform_find_maintenance_window_params/latest.ts similarity index 86% rename from x-pack/packages/security-solution/common/src/flyout/panels/keys.ts rename to x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/transforms/transform_find_maintenance_window_params/latest.ts index fe06cf652d016..25300c97a6d2e 100644 --- a/x-pack/packages/security-solution/common/src/flyout/panels/keys.ts +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/transforms/transform_find_maintenance_window_params/latest.ts @@ -5,4 +5,4 @@ * 2.0. */ -export const HOST_PANEL = 'host-panel'; +export * from './v1'; diff --git a/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/transforms/transform_find_maintenance_window_params/v1.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/transforms/transform_find_maintenance_window_params/v1.ts new file mode 100644 index 0000000000000..c59f5d189716e --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/transforms/transform_find_maintenance_window_params/v1.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 { FindMaintenanceWindowsRequestQuery } from '../../../../../../../common/routes/maintenance_window/apis/find'; +import { FindMaintenanceWindowsParams } from '../../../../../../application/maintenance_window/methods/find/types'; + +export const transformFindMaintenanceWindowParams = ( + params: FindMaintenanceWindowsRequestQuery +): FindMaintenanceWindowsParams => ({ + ...(params.page ? { page: params.page } : {}), + ...(params.per_page ? { perPage: params.per_page } : {}), +}); diff --git a/x-pack/packages/security-solution/common/src/flyout/panels/index.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/transforms/transform_find_maintenance_window_to_response/latest.ts similarity index 82% rename from x-pack/packages/security-solution/common/src/flyout/panels/index.ts rename to x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/transforms/transform_find_maintenance_window_to_response/latest.ts index 3134078ebdf7f..25300c97a6d2e 100644 --- a/x-pack/packages/security-solution/common/src/flyout/panels/index.ts +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/transforms/transform_find_maintenance_window_to_response/latest.ts @@ -5,5 +5,4 @@ * 2.0. */ -export * from './host/right'; -export * from './keys'; +export * from './v1'; diff --git a/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/transforms/transform_find_maintenance_window_to_response/v1.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/transforms/transform_find_maintenance_window_to_response/v1.ts new file mode 100644 index 0000000000000..9110914a998ca --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/apis/find/transforms/transform_find_maintenance_window_to_response/v1.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 { transformMaintenanceWindowToResponseV1 } from '../../../../transforms'; +import type { FindMaintenanceWindowsResponseV1 } from '../../../../../../../common/routes/maintenance_window/apis/find'; +import type { MaintenanceWindow } from '../../../../../../application/maintenance_window/types'; +import type { FindMaintenanceWindowsResult } from '../../../../../../application/maintenance_window/methods/find/types'; + +export const transformFindMaintenanceWindowResponse = ( + result: FindMaintenanceWindowsResult +): FindMaintenanceWindowsResponseV1 => { + return { + page: result.page, + per_page: result.perPage, + total: result.total, + data: result.data.map((maintenanceWindow: MaintenanceWindow) => + transformMaintenanceWindowToResponseV1(maintenanceWindow) + ), + }; +}; diff --git a/x-pack/plugins/canvas/server/plugin.ts b/x-pack/plugins/canvas/server/plugin.ts index 074d29ec977f9..bf15dd31100ba 100644 --- a/x-pack/plugins/canvas/server/plugin.ts +++ b/x-pack/plugins/canvas/server/plugin.ts @@ -19,7 +19,6 @@ import { ReportingServerPluginSetup } from '@kbn/reporting-server'; import { getCanvasFeature } from './feature'; import { initRoutes } from './routes'; import { registerCanvasUsageCollector } from './collectors'; -import { loadSampleData } from './sample_data'; import { setupInterpreter } from './setup_interpreter'; import { customElementType, workpadTypeFactory, workpadTemplateType } from './saved_objects'; import type { CanvasSavedObjectTypeMigrationsDeps } from './saved_objects/migrations'; @@ -82,11 +81,6 @@ export class CanvasPlugin implements Plugin { logger: this.logger, }); - loadSampleData( - plugins.home.sampleData.addSavedObjectsToSampleDataset, - plugins.home.sampleData.addAppLinksToSampleDataset - ); - const getIndexForType = (type: string) => coreSetup .getStartServices() diff --git a/x-pack/plugins/canvas/server/sample_data/ecommerce_saved_objects.json b/x-pack/plugins/canvas/server/sample_data/ecommerce_saved_objects.json deleted file mode 100644 index be5bc213e59a4..0000000000000 --- a/x-pack/plugins/canvas/server/sample_data/ecommerce_saved_objects.json +++ /dev/null @@ -1,1220 +0,0 @@ -[ - { - "id": "workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e", - "type": "canvas-workpad", - "updated_at": "2018-10-22T15:19:02.081Z", - "version": 1, - "migrationVersion": { - "canvas-workpad": "7.0.0" - }, - "attributes": { - "name": "[eCommerce] Revenue Tracking", - "width": 1080, - "height": 720, - "page": 0, - "pages": [ - { - "id": "page-21cffafb-9eda-47f9-b35a-67a92e605abe", - "style": { - "background": "#fff" - }, - "elements": [ - { - "id": "element-3a220f56-0729-4464-b4fd-7975a8396d24", - "position": { - "left": 641, - "top": 97, - "width": 175, - "height": 510, - "angle": 0 - }, - "expression": "essql query=\"select COUNT(*) as total_count, SUM(CASE WHEN customer_gender='MALE' THEN 1 else 0 END) as male_count from kibana_sample_data_ecommerce\"\n| math \"male_count / total_count\" \n| revealImage origin=\"bottom\" image={asset \"asset-aaa14d64-2c1c-47f2-95c0-21306ee18cba\"} emptyImage={asset \"asset-960c8c6e-da72-412d-9d04-34a98cdb5760\"}" - }, - { - "id": "element-4a3fef74-5d8c-4bbe-8f3f-fe55afdd4b60", - "position": { - "left": 627.5, - "top": 82.5, - "width": 197, - "height": 521, - "angle": 0 - }, - "expression": "image mode=\"contain\" dataurl={asset \"asset-8ae4b612-43a3-4846-8f0d-abb9785e95c3\"}" - }, - { - "id": "element-fb2761a1-df28-411a-8614-dbee0f437cfe", - "position": { - "left": 446, - "top": 93, - "width": 238, - "height": 520, - "angle": 0 - }, - "expression": "essql query=\"select COUNT(*) as total_count, SUM(CASE WHEN customer_gender='FEMALE' THEN 1 else 0 END) as female_count from kibana_sample_data_ecommerce\"\n| math \"female_count / total_count\" \n| revealImage origin=\"bottom\" image={asset \"asset-2f64bd10-953d-4163-90e9-a55e9ca4c52a\"} emptyImage={asset \"asset-3a26727a-b756-44be-a82c-273dd85bda09\"}", - "filter": null - }, - { - "id": "element-46b2f8df-f7db-4502-9cd0-b33c1c70b8b1", - "position": { - "left": 275, - "top": 306, - "width": 105, - "height": 102, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Women's Clothing\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 2)'}\n| pointseries color=category size=percentage\n| pie hole=\"65\" labels=false seriesStyle={seriesStyle label=\"Women's Clothing\" color=\"#eb6c66\"}\n seriesStyle={seriesStyle label=\"Other\" color=\"#ede9e7\"}" - }, - { - "id": "element-eaa7f9a9-54ca-4c7c-9d27-62312d92a264", - "position": { - "left": 275, - "top": 157, - "width": 105, - "height": 102, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Women's Accessories\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 2)'}\n| pointseries color=category size=percentage\n| pie hole=\"65\" labels=false seriesStyle={seriesStyle label=\"Women's Accessories\" color=\"#eb6c66\"}\n seriesStyle={seriesStyle label=\"Other\" color=\"#ede9e7\"}" - }, - { - "id": "element-58dfc1e8-a470-447f-8107-62bebe955475", - "position": { - "left": 275, - "top": 450, - "width": 106, - "height": 102, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Women's Shoes\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 2)'}\n| pointseries color=category size=percentage\n| pie hole=\"65\" labels=false seriesStyle={seriesStyle label=\"Women's Shoes\" color=\"#eb6c66\"}\n seriesStyle={seriesStyle label=\"Other\" color=\"#ede9e7\"}" - }, - { - "id": "element-c0a5f8d9-f52f-4e0f-a8a4-08c1b7984bb2", - "position": { - "left": 893.5, - "top": 157, - "width": 105, - "height": 102, - "angle": 0 - }, - "expression": "essql \"SELECT category, COUNT(category) AS count FROM \\\"kibana_sample_data_ecommerce\\\" GROUP BY category\" \n| mapColumn \"category\" fn={if {getCell \"category\" | eq \"Men's Accessories\"} then={getCell \"category\"} else=\"Other\"} \n| ply by=\"category\" fn={math \"sum(count)\" | as \"count\"} | staticColumn \"total\" value={math \"sum(count)\"} \n| mapColumn \"percentage\" fn={math \"round(count/total * 100, 2)\"} \n| pointseries color=\"category\" size=\"percentage\" \n| pie hole=\"65\" labels=false seriesStyle={seriesStyle label=\"Men's Accessories\" color=\"#f8bd4a\"}\n seriesStyle={seriesStyle label=\"Other\" color=\"#ede9e7\"}" - }, - { - "id": "element-060eb797-c583-4c8f-b0e1-3ab9b58c4cf5", - "position": { - "left": 894, - "top": 305, - "width": 105, - "height": 102, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Men's Clothing\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 2)'}\n| pointseries color=category size=percentage\n| pie hole=\"65\" labels=false seriesStyle={seriesStyle label=\"Men's Clothing\" color=\"#f8bd4a\"}\n seriesStyle={seriesStyle label=\"Other\" color=\"#ede9e7\"}" - }, - { - "id": "element-8f811cd0-e4b2-48b3-a96f-b8384179bbfb", - "position": { - "left": 894, - "top": 451, - "width": 105, - "height": 102, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Men's Shoes\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 2)'}\n| pointseries color=category size=percentage\n| pie hole=\"65\" labels=false seriesStyle={seriesStyle label=\"Men's Shoes\" color=\"#f8bd4a\"}\n seriesStyle={seriesStyle label=\"Other\" color=\"#ede9e7\"}" - }, - { - "id": "element-c0595f68-81c5-43e7-9ecf-808e8cfe48c0", - "position": { - "left": 298, - "top": 517, - "width": 93, - "height": 44, - "angle": 0 - }, - "expression": "image mode=\"contain\" dataurl={asset \"asset-66a89124-fc39-4109-8acd-e1ac7f382e04\"} | render" - }, - { - "id": "element-0a14f255-6445-4860-b41d-d1c0b7ca9c36", - "position": { - "left": 890, - "top": 516, - "width": 102, - "height": 54, - "angle": 0 - }, - "expression": "image mode=\"contain\" dataurl={asset \"asset-4acbecf1-e514-4e3b-bce4-61920335941e\"} | render" - }, - { - "id": "element-6aeefa4f-6b4f-4a6c-99ff-2cca052f5be6", - "position": { - "top": 291, - "left": 960, - "height": 121, - "width": 95, - "angle": 12.8 - }, - "expression": "image mode=\"contain\" dataurl={asset \"asset-ae290f99-dd16-41a4-8191-7dd7be154be6\"} | render" - }, - { - "id": "element-68d9cb72-7a0e-4cac-996a-0ec8356f0df8", - "position": { - "left": 245, - "top": 291, - "width": 62, - "height": 139, - "angle": -1 - }, - "expression": "image mode=\"contain\" dataurl={asset \"asset-565cd264-9196-4ebd-9d6e-f413f1db734d\"} | render" - }, - { - "id": "element-9bac6c19-517c-46e6-8d71-9273910366d1", - "position": { - "left": 322, - "top": 230, - "width": 60, - "height": 27, - "angle": 10 - }, - "expression": "image mode=\"contain\" dataurl={asset \"asset-6222f3e0-1dab-4aa9-a06c-63d7732cc5f4\"} | render" - }, - { - "id": "element-a58f99d3-8ea4-474e-9aba-e7ed3e91a178", - "position": { - "left": 892, - "top": 235, - "width": 64, - "height": 24, - "angle": 7 - }, - "expression": "image mode=\"contain\" dataurl={asset \"asset-f8ac482e-0c7a-4cac-8bcf-6f4f55300081\"} | render" - }, - { - "id": "element-84dec4fd-ee5d-49c0-9600-41b951033e09", - "position": { - "left": 394, - "top": 393.0200895724113, - "width": 94, - "height": 56, - "angle": 0 - }, - "expression": "essql query=\"select COUNT(*) as total_count, SUM(CASE WHEN customer_gender='FEMALE' THEN 1 else 0 END) as female_count from kibana_sample_data_ecommerce\"\n| math \"round(100 * female_count / total_count)\" | markdown {context} \"%\" font={font family=\"Avenir\" size=48 align=\"center\" color=\"#eb6c66\" weight=\"normal\" underline=false italic=false}" - }, - { - "id": "element-9e0b6230-2bc9-4995-8207-043e3063faeb", - "position": { - "left": 794, - "top": 369, - "width": 94, - "height": 56, - "angle": 0 - }, - "expression": "essql query=\"select COUNT(*) as total_count, SUM(CASE WHEN customer_gender='MALE' THEN 1 else 0 END) as male_count from kibana_sample_data_ecommerce\"\n| math \"round(100 * male_count / total_count)\" | markdown {context} \"%\" font={font family=\"Avenir\" size=48 align=\"center\" color=\"#f8bd4a\" weight=\"normal\" underline=false italic=false}" - }, - { - "id": "element-2185edff-ac50-4162-b583-3bfd6469e925", - "position": { - "left": -3, - "top": -1, - "width": 214, - "height": 721, - "angle": 0 - }, - "expression": "markdown \"\" | render containerStyle={containerStyle backgroundColor=\"#ede9e7\"}" - }, - { - "id": "element-71b63f54-0961-4ed2-a85d-45584b48a631", - "position": { - "left": 8, - "top": 35, - "width": 122, - "height": 29, - "angle": 0 - }, - "expression": "markdown \"TOTAL SALES\" font={font family=\"'Avenir', Helvetica, Arial, sans-serif\" size=18 align=\"left\" color=\"#777\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-61fdb4ce-5dea-4699-b958-6c40e2a2b61a", - "position": { - "left": 398, - "top": 381.5200895724113, - "width": 78, - "height": 23, - "angle": 0 - }, - "expression": "markdown \"WOMEN\" font={font family=\"Avenir\" size=18 align=\"left\" color=\"#999\" weight=\"normal\" underline=false italic=false}" - }, - { - "id": "element-157ab7a7-e3ef-477f-8c97-84c67f7ab28e", - "position": { - "left": 840, - "top": 352, - "width": 46, - "height": 23, - "angle": 0 - }, - "expression": "markdown \"MEN\" font={font family=\"Avenir\" size=18 align=\"left\" color=\"#999\" weight=\"normal\" underline=false italic=false} | render" - }, - { - "id": "element-bfa6f8bc-c083-4817-a682-91eb50fc214d", - "position": { - "left": 299, - "top": 344, - "width": 60, - "height": 34, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Women's Clothing\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 0)'}\n| filterrows {getCell \"category\" | any {eq \"Women's Clothing\"}}\n| markdown {getCell \"percentage\"} \"%\" font={font family=\"Avenir\" size=24 align=\"center\" color=\"#000\" weight=\"normal\" underline=false italic=false}" - }, - { - "id": "element-73e918d2-14d0-4ed6-9cfe-204ad4eaff24", - "position": { - "left": 300, - "top": 488, - "width": 59, - "height": 30, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Women's Shoes\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 0)'}\n| filterrows {getCell \"category\" | any {eq \"Women's Shoes\"}}\n| markdown {getCell \"percentage\"} \"%\" font={font family=\"Avenir\" size=24 align=\"center\" color=\"#000\" weight=\"normal\" underline=false italic=false}" - }, - { - "id": "element-d9dd8a8e-3af7-4e59-9115-3fd13c312d39", - "position": { - "left": 297, - "top": 194, - "width": 62, - "height": 37, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Women's Accessories\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 0)'}\n| filterrows {getCell \"category\" | any {eq \"Women's Accessories\"}}\n| markdown {getCell \"percentage\"} \"%\" font={font family=\"Avenir\" size=24 align=\"center\" color=\"#000\" weight=\"normal\" underline=false italic=false}" - }, - { - "id": "element-af25bb55-818c-4c69-b2ae-45245af131e6", - "position": { - "left": 923, - "top": 194, - "width": 51, - "height": 34, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Men's Accessories\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 0)'}\n| filterrows {getCell \"category\" | any {eq \"Men's Accessories\"}}\n| markdown {getCell \"percentage\"} \"%\" font={font family=\"Avenir\" size=24 align=\"center\" color=\"#000\" weight=\"normal\" underline=false italic=false}" - }, - { - "id": "element-9e02f39b-14b2-42a4-ae32-a4c292ece6ba", - "position": { - "left": 924, - "top": 343, - "width": 50, - "height": 33, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Men's Clothing\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 0)'}\n| filterrows {getCell \"category\" | any {eq \"Men's Clothing\"}}\n| markdown {getCell \"percentage\"} \"%\" font={font family=\"Avenir\" size=24 align=\"center\" color=\"#000\" weight=\"normal\" underline=false italic=false}" - }, - { - "id": "element-b5f950b0-bb0f-462d-a92f-35bc3f1f4c39", - "position": { - "left": 921, - "top": 489, - "width": 50, - "height": 33, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Men's Shoes\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 0)'}\n| filterrows {getCell \"category\" | any {eq \"Men's Shoes\"}}\n| markdown {getCell \"percentage\"} \"%\" font={font family=\"Avenir\" size=24 align=\"center\" color=\"#000\" weight=\"normal\" underline=false italic=false}" - }, - { - "id": "element-a4d410df-83b8-40dc-987f-e2c1380874a6", - "position": { - "left": -135.50000000000006, - "top": 330.5, - "width": 500, - "height": 217, - "angle": 90 - }, - "expression": "timelion \n \".es(index=kibana_sample_data_ecommerce, metric=sum:taxless_total_price, timefield=order_date)\" interval=\"1d\" from={essql \"SELECT order_date as min FROM kibana_sample_data_ecommerce order by order_date limit 1\" | getCell min} to={essql \"SELECT order_date as max FROM kibana_sample_data_ecommerce order by order_date DESC limit 1\" | getCell max}\n| pointseries x=\"@timestamp\" y=\"value\"\n| plot yaxis=false defaultStyle={seriesStyle points=\"0\" bars=\"50000000\" lines=\"0\" color=\"#62bb96\"} font={font size=12 family=\"Avenir\" color=\"#999\" align=\"left\"}", - "filter": null - }, - { - "id": "element-ccbb192a-725b-4479-a34b-9d70b0fa1a8a", - "position": { - "left": 441, - "top": 93, - "width": 242, - "height": 520, - "angle": 0 - }, - "expression": "image mode=\"contain\" dataurl={asset \"asset-93e415d8-82c6-47d4-8c55-e38df329b88b\"}" - }, - { - "id": "element-18fc190c-e46a-408b-a220-8e1745eb77e6", - "position": { - "left": 394, - "top": 451, - "width": 115, - "height": 2, - "angle": 0 - }, - "expression": "markdown \"\" | render containerStyle={containerStyle backgroundColor=\"#000000\"}", - "filter": null - }, - { - "id": "element-1eb4fcd1-f8dd-4bd9-98a6-c5cbd4bcc8dc", - "position": { - "left": 767, - "top": 431, - "width": 119, - "height": 2, - "angle": 0 - }, - "expression": "markdown \"\" | render containerStyle={containerStyle backgroundColor=\"#000000\"}", - "filter": null - }, - { - "id": "element-2f976ef1-abc7-4bd2-82cb-cc114431eaea", - "position": { - "left": 744.5, - "top": 410, - "width": 35, - "height": 50, - "angle": 0 - }, - "expression": "markdown \"## ○\" font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=14 align=\"center\" color=\"#000000\" weight=\"normal\" underline=false italic=false} | render containerStyle={containerStyle}" - }, - { - "id": "element-532638f8-758e-4cf4-86f0-c4d896e4477d", - "position": { - "left": 497, - "top": 429.4483902138134, - "width": 35, - "height": 50, - "angle": 0 - }, - "expression": "markdown \"## ○\" font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=14 align=\"center\" color=\"#000000\" weight=\"normal\" underline=false italic=false} | render containerStyle={containerStyle}", - "filter": null - }, - { - "id": "element-62bf98c1-bd0c-40dd-ae44-abbcc50c0ae1", - "position": { - "left": -47, - "top": 410, - "width": 477, - "height": 63, - "angle": 90 - }, - "expression": "timelion \n \".es(index=kibana_sample_data_ecommerce, metric=sum:taxless_total_price, timefield=order_date)\" interval=\"1w\"\n| mapColumn \"value\" fn={math \"round(value,0)\"}\n| staticColumn \"mean\" value={math \"round(mean(value),0)\"}\n| mapColumn \"mean\" \"as\" \"rnd\" fn={math \"round(random(0.9,1.1),2)\"}\n| mapColumn \"value\" \"as\" \"final\" fn={math \"round(multiply(rnd,mean),2)\"}\n| pointseries x=\"@timestamp\" y=\"final\"\n| plot xaxis=false yaxis=false defaultStyle={seriesStyle points=\"0\" bars=\"0\" lines=\"3\" color=\"#eb6c66\"}\n| render containerStyle={containerStyle opacity=\"0.5\"}", - "filter": null - }, - { - "id": "element-e1b5a809-aed3-42e0-8806-46b0e462a9d4", - "position": { - "left": 488, - "top": 661, - "width": 279, - "height": 38, - "angle": 0 - }, - "expression": "markdown \"REVENUE BY CATEGORY\" font={font family=\"'Avenir', Helvetica, Arial, sans-serif\" size=24 align=\"left\" color=\"#999\" weight=\"normal\" underline=false italic=false}", - "filter": null - }, - { - "id": "element-50ea8d53-383d-4582-9497-0692a81d9df8", - "position": { - "left": -7, - "top": 673, - "width": 219, - "height": 22, - "angle": 0 - }, - "expression": "markdown \"REVENUE BY DAY\" \n font={font family=\"'Avenir', Helvetica, Arial, sans-serif\" size=14 align=\"center\" color=\"#62bb96\" weight=\"normal\" underline=false italic=false}", - "filter": null - }, - { - "id": "element-de932ce1-1c99-448b-bfb4-a98f91add877", - "position": { - "left": -7, - "top": 689, - "width": 219, - "height": 22, - "angle": 0 - }, - "expression": "markdown \"SALES PREDICTION\" \n font={font family=\"'Avenir', Helvetica, Arial, sans-serif\" size=10 align=\"center\" color=\"#eb6c66\" weight=\"normal\" underline=false italic=false}", - "filter": null - }, - { - "id": "element-f5f539dd-1abe-42a9-bb2a-1d2a07ff108c", - "position": { - "left": 286, - "top": 263, - "width": 76, - "height": 16, - "angle": 0 - }, - "expression": "markdown \"ACCESSORIES\" \n font={font family=\"'Avenir', Helvetica, Arial, sans-serif\" size=10 align=\"center\" color=\"#999\" weight=\"normal\" underline=false italic=false}", - "filter": null - }, - { - "id": "element-1eb8a1d1-beff-4d42-8b82-cddc28075bb0", - "position": { - "left": 911, - "top": 262, - "width": 76, - "height": 16, - "angle": 0 - }, - "expression": "markdown \"ACCESSORIES\" \n font={font family=\"'Avenir', Helvetica, Arial, sans-serif\" size=10 align=\"center\" color=\"#999\" weight=\"normal\" underline=false italic=false}", - "filter": null - }, - { - "id": "element-d36fd045-500a-494c-8d1f-281c03a9720d", - "position": { - "left": 909, - "top": 409, - "width": 76, - "height": 16, - "angle": 0 - }, - "expression": "markdown \"CLOTHING\" \n font={font family=\"'Avenir', Helvetica, Arial, sans-serif\" size=10 align=\"center\" color=\"#999\" weight=\"normal\" underline=false italic=false}", - "filter": null - }, - { - "id": "element-3388ba84-9109-4f1e-bb3b-8e7e4247b99e", - "position": { - "left": 291, - "top": 411, - "width": 76, - "height": 16, - "angle": 0 - }, - "expression": "markdown \"CLOTHING\" \n font={font family=\"'Avenir', Helvetica, Arial, sans-serif\" size=10 align=\"center\" color=\"#999\" weight=\"normal\" underline=false italic=false}", - "filter": null - }, - { - "id": "element-4fe5f99e-ffea-4d21-9718-dbfbbd161a64", - "position": { - "left": 910, - "top": 570, - "width": 76, - "height": 16, - "angle": 0 - }, - "expression": "markdown \"SHOES\" \n font={font family=\"'Avenir', Helvetica, Arial, sans-serif\" size=10 align=\"center\" color=\"#999\" weight=\"normal\" underline=false italic=false}", - "filter": null - }, - { - "id": "element-a6b5a208-6a75-42ad-8a1c-aa50359ecd44", - "position": { - "left": 293, - "top": 562, - "width": 76, - "height": 16, - "angle": 0 - }, - "expression": "markdown \"SHOES\" \n font={font family=\"'Avenir', Helvetica, Arial, sans-serif\" size=10 align=\"center\" color=\"#999\" weight=\"normal\" underline=false italic=false}", - "filter": null - }, - { - "id": "element-45310e03-f6dd-4283-aa04-8d27a4501665", - "position": { - "left": 5.999999999999915, - "top": 76.5, - "width": 201.00000000000009, - "height": 68.5, - "angle": 0 - }, - "expression": "essql \n query=\"SELECT sum(taxless_total_price) AS sum_total_price FROM \\\"kibana_sample_data_ecommerce\\\"\"\n| math \"sum_total_price\"\n| formatNumber \"$0a\"\n| metric \n metricFont={font family=\"'Avenir', Helvetica, Arial, sans-serif\" size=72 align=\"left\" color=\"#000000\" weight=\"normal\" underline=false italic=false}\n| render" - } - ] - }, - { - "id": "page-f704531f-3a72-4f29-a199-7e00d0c1ffef", - "style": { - "background": "#FAF0DD" - }, - "elements": [ - { - "id": "element-2c7e18fa-480f-4e3b-b4df-f649687229c6", - "position": { - "left": 788, - "top": 112.12093494719988, - "width": 204, - "height": 202, - "angle": 0 - }, - "expression": "essql query=\"select COUNT(*) as total_count, SUM(CASE WHEN customer_gender='MALE' THEN 1 else 0 END) as male_count from kibana_sample_data_ecommerce\"\n| math \"male_count / total_count\" \n| revealImage origin=\"bottom\" image={asset \"asset-803ec373-2608-4f6f-8cf9-0dbb2f6437ce\"} emptyImage={asset \"asset-18070a2a-cd01-410a-ba89-a4505e2fbc5b\"}" - }, - { - "id": "element-2379c3ca-2c31-4948-8412-d14115500efc", - "position": { - "left": 788, - "top": 369.6303606465582, - "width": 204, - "height": 202, - "angle": 0 - }, - "expression": "essql query=\"select COUNT(*) as total_count, SUM(CASE WHEN customer_gender='FEMALE' THEN 1 else 0 END) as female_count from kibana_sample_data_ecommerce\"\n| math \"female_count / total_count\" \n| revealImage origin=\"bottom\" image={asset \"asset-e644a484-4097-40b9-a08e-7250ba963059\"} emptyImage={asset \"asset-7e4f7119-b2d8-4527-9bd8-887cb25974e7\"}" - }, - { - "id": "element-3f52813f-7d0e-4ec7-9aad-c731b670d88d", - "position": { - "left": -69, - "top": 400.19203602130153, - "width": 388, - "height": 141, - "angle": 90 - }, - "expression": "timelion \n \".es(index=kibana_sample_data_ecommerce, metric=sum:taxless_total_price, timefield=order_date)\" interval=\"1d\" from={essql \"SELECT order_date as min FROM kibana_sample_data_ecommerce order by order_date limit 1\" | getCell min} to={essql \"SELECT order_date as max FROM kibana_sample_data_ecommerce order by order_date DESC limit 1\" | getCell max}\n| pointseries x=\"@timestamp\" y=\"value\"\n| plot yaxis=false defaultStyle={seriesStyle points=\"0\" bars=\"50000000\" lines=\"0\" color=\"#00A89C\"} \n font={font family=\"gilroy extrabold, Avenir\" size=16 align=\"left\" color=\"#F05A24\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-dc86d147-0611-4ce8-9b0b-e95fc6f8ab80", - "position": { - "left": 244, - "top": 138, - "width": 157, - "height": 145, - "angle": -45 - }, - "expression": "essql \"SELECT category, COUNT(category) AS count FROM \\\"kibana_sample_data_ecommerce\\\" GROUP BY category\" \n| mapColumn \"category\" fn={if {getCell \"category\" | eq \"Men's Accessories\"} then={getCell \"category\"} else=\"Other\"} \n| ply by=\"category\" fn={math \"sum(count)\" | as \"count\"} | staticColumn \"total\" value={math \"sum(count)\"} \n| mapColumn \"percentage\" fn={math \"round(count/total * 100, 2)\"} \n| pointseries color=\"category\" size=\"percentage\"\n| pie hole=\"85\" labels=false seriesStyle={seriesStyle label=\"Men's Accessories\" color=\"#00A89C\"}\n seriesStyle={seriesStyle label=\"Other\" color=\"#CBCBCB\"}" - }, - { - "id": "element-29967798-174d-4109-b229-e776b0a4bf8c", - "position": { - "left": 425.5, - "top": 138, - "width": 157, - "height": 145, - "angle": -99 - }, - "expression": "essql \n \"SELECT category, COUNT(category) AS count FROM \\\"kibana_sample_data_ecommerce\\\" GROUP BY category ORDER BY category\"\n| mapColumn \"category\" \n fn={if {getCell \"category\" | eq \"Men's Clothing\"} then={getCell \"category\"} else=\"Other\"}\n| ply by=\"category\" fn={math \"sum(count)\" | as \"count\"}\n| staticColumn \"total\" value={math \"sum(count)\"}\n| mapColumn \"percentage\" fn={math \"round(count/total * 100, 2)\"}\n| sort \"category\"\n| pointseries color=\"category\" size=\"percentage\"\n| pie hole=\"85\" labels=false seriesStyle={seriesStyle label=\"Men's Clothing\" color=\"#00A89C\"}\n seriesStyle={seriesStyle label=\"Other\" color=\"#CBCBCB\"}" - }, - { - "id": "element-abaa4e35-cfc0-4171-8c34-0914cea35082", - "position": { - "left": 610, - "top": 136, - "width": 157, - "height": 145, - "angle": -56 - }, - "expression": "essql \"SELECT category, COUNT(category) AS count FROM \\\"kibana_sample_data_ecommerce\\\" GROUP BY category\" \n| mapColumn \"category\" fn={if {getCell \"category\" | eq \"Men's Shoes\"} then={getCell \"category\"} else=\"Other\"} \n| ply by=\"category\" fn={math \"sum(count)\" | as \"count\"} | staticColumn \"total\" value={math \"sum(count)\"} \n| mapColumn \"percentage\" fn={math \"round(count/total * 100, 2)\"} \n| pointseries color=\"category\" size=\"percentage\"\n| pie hole=\"85\" labels=false seriesStyle={seriesStyle label=\"Men's Shoes\" color=\"#00A89C\"}\n seriesStyle={seriesStyle label=\"Other\" color=\"#CBCBCB\"}" - }, - { - "id": "element-20da5d5d-2b29-4888-a304-a14377d727ec", - "position": { - "left": 245, - "top": 383, - "width": 157, - "height": 145, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Women's Accessories\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 2)'}\n| pointseries color=category size=percentage\n| pie hole=\"85\" labels=false palette={palette \"#CBCBCB\" \"#F05A24\" gradient=false} seriesStyle={seriesStyle label=\"Women's Accessories\" color=\"#F05A24\"}\n seriesStyle={seriesStyle label=\"Other\" color=\"#CBCBCB\"}" - }, - { - "id": "element-9dd99310-e0a6-4ab7-b049-c7ea63180b4e", - "position": { - "left": 429, - "top": 380, - "width": 157, - "height": 145, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Women's Clothing\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 2)'}\n| pointseries color=category size=percentage\n| pie hole=\"85\" labels=false seriesStyle={seriesStyle label=\"Women's Clothing\" color=\"#F05A24\"}\n seriesStyle={seriesStyle label=\"Other\" color=\"#CBCBCB\"}" - }, - { - "id": "element-715b318d-fe6d-42b2-abea-b238b0daa8c7", - "position": { - "left": 610, - "top": 384, - "width": 157, - "height": 145, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Women's Shoes\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 2)'}\n| pointseries color=category size=percentage\n| pie hole=\"85\" labels=false seriesStyle={seriesStyle label=\"Women's Shoes\" color=\"#F05A24\"}\n seriesStyle={seriesStyle label=\"Other\" color=\"#CBCBCB\"}" - }, - { - "id": "element-9c27c6d0-b9d9-4adb-a5f3-abb7a3403977", - "position": { - "left": 239, - "top": 157, - "width": 172, - "height": 102, - "angle": 0 - }, - "expression": "image mode=\"contain\" dataurl={asset \"asset-e79711e8-d9da-45e1-a234-9efe226a444d\"}\n| render" - }, - { - "id": "element-c9b7f707-f27c-44cc-b9f1-e7b693c66702", - "position": { - "left": 463, - "top": 165, - "width": 85, - "height": 86, - "angle": 0 - }, - "expression": "image mode=\"contain\" dataurl={asset \"asset-9493e336-1b11-4e61-bad2-716c46194550\"}\n| render" - }, - { - "id": "element-8bcb4071-0012-46da-9316-524c06bb673a", - "position": { - "left": 648, - "top": 165, - "width": 85, - "height": 86, - "angle": 0 - }, - "expression": "image mode=\"contain\" dataurl={asset \"asset-23f2bfe9-e58c-4a56-98c6-fad59eecdf74\"}\n| render" - }, - { - "id": "element-3532c81f-a341-4d23-90b6-68912c04ee46", - "position": { - "left": 1016, - "top": 391, - "width": 78, - "height": 29, - "angle": 0 - }, - "expression": "markdown \"W\" \n font={font family=\"gilroy extrabold, Avenir\" size=24 align=\"left\" color=\"#F05A24\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-2673d24b-2706-4ffe-a38b-5736c3bc616c", - "position": { - "left": 1018, - "top": 420, - "width": 78, - "height": 29, - "angle": 0 - }, - "expression": "markdown \"O\" \n font={font family=\"gilroy extrabold, Avenir\" size=24 align=\"left\" color=\"#F05A24\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-5d2b6fe8-34a9-49bc-8229-4e1c2b8029e1", - "position": { - "left": 1018, - "top": 446, - "width": 78, - "height": 29, - "angle": 0 - }, - "expression": "markdown \"M\" \n font={font family=\"gilroy extrabold, Avenir\" size=24 align=\"left\" color=\"#F05A24\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-b67e44c0-afe0-4f15-b521-af0759348dc6", - "position": { - "left": 1020, - "top": 475, - "width": 78, - "height": 29, - "angle": 0 - }, - "expression": "markdown \"E\" \n font={font family=\"gilroy extrabold, Avenir\" size=24 align=\"left\" color=\"#F05A24\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-370b7910-dd0b-4257-90de-229d25bd6610", - "position": { - "left": 1019, - "top": 504, - "width": 78, - "height": 29, - "angle": 0 - }, - "expression": "markdown \"N\" \n font={font family=\"gilroy extrabold, Avenir\" size=24 align=\"left\" color=\"#F05A24\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-cbdd2309-101d-4edc-874c-47c3e6c99df5", - "position": { - "left": 1018, - "top": 173, - "width": 78, - "height": 29, - "angle": 0 - }, - "expression": "markdown \"M\" \n font={font family=\"gilroy extrabold, Avenir\" size=24 align=\"left\" color=\"#00A89C\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-7288cfda-673f-4741-bec2-4675bc4952ed", - "position": { - "left": 1020, - "top": 201, - "width": 78, - "height": 29, - "angle": 0 - }, - "expression": "markdown \"E\" \n font={font family=\"gilroy extrabold, Avenir\" size=24 align=\"left\" color=\"#00A89C\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-e68822cc-f4b7-48a4-a8b2-86d84d8ddeb1", - "position": { - "left": 1019, - "top": 229, - "width": 78, - "height": 29, - "angle": 0 - }, - "expression": "markdown \"N\" \n font={font family=\"gilroy extrabold, Avenir\" size=24 align=\"left\" color=\"#00A89C\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-f446c8e1-b65e-4a1f-8476-e80e437a31ec", - "position": { - "left": 240, - "top": 414, - "width": 169, - "height": 100, - "angle": 0 - }, - "expression": "image mode=\"contain\" dataurl={asset \"asset-86d05b5e-1a4b-4979-95e9-7071b9923470\"}\n| render" - }, - { - "id": "element-5406c1e9-9626-413c-9eaa-a73ca1e10b8c", - "position": { - "left": 464, - "top": 414, - "width": 85, - "height": 86, - "angle": 0 - }, - "expression": "image mode=\"contain\" dataurl={asset \"asset-fdfc9cc7-2c6a-44fe-b9be-c4ff115c92c1\"}\n| render" - }, - { - "id": "element-d3fe9442-583b-4acb-a700-3935adb4316c", - "position": { - "left": 654, - "top": 419, - "width": 74, - "height": 76, - "angle": 0 - }, - "expression": "image mode=\"contain\" dataurl={asset \"asset-58ae3445-4001-45e7-9603-19ec8d41e64e\"}\n| render" - }, - { - "id": "element-28ffc136-8702-4cfc-9643-9825dfd7a6a3", - "position": { - "left": 48, - "top": 247, - "width": 118, - "height": 22, - "angle": 0 - }, - "expression": "markdown \"P R O G R E S S\" \n font={font family=\"gilroy extrabold, Avenir\" size=12 align=\"center\" color=\"#F05A24\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-c6b853cc-72a3-4110-b40b-359ecb7b1af6", - "position": { - "left": 92, - "top": 268, - "width": 27, - "height": 4, - "angle": 0 - }, - "expression": "markdown \"\"\n| render containerStyle={containerStyle backgroundColor=\"#F05A24\"}" - }, - { - "id": "element-692a539e-0c3b-4e8d-bc3e-3cf84ddd53c7", - "position": { - "left": 90, - "top": 98, - "width": 27, - "height": 4, - "angle": 0 - }, - "expression": "markdown \"\"\n| render containerStyle={containerStyle backgroundColor=\"#F05A24\"}" - }, - { - "id": "element-99bed359-ef39-403d-bcb3-81cf8d1f6c62", - "position": { - "left": 44, - "top": 77, - "width": 118, - "height": 22, - "angle": 0 - }, - "expression": "markdown \"T O T A L\" \n font={font family=\"gilroy extrabold, Avenir\" size=12 align=\"center\" color=\"#F05A24\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-8d72d271-b12e-41af-a18b-795490cf7f38", - "position": { - "left": 276, - "top": 290, - "width": 93, - "height": 40, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Men's Accessories\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 0)'}\n| filterrows {getCell \"category\" | any {eq \"Men's Accessories\"}}\n| markdown {getCell \"percentage\"} font={font family=\"nexa bold, Avenir\" size=36 align=\"center\" color=\"#00A89C\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-cc17144e-096c-47b1-85dc-047724cc095e", - "position": { - "left": 465, - "top": 291, - "width": 78, - "height": 40, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Men's Clothing\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 0)'}\n| filterrows {getCell \"category\" | any {eq \"Men's Clothing\"}}\n| markdown {getCell \"percentage\"} font={font family=\"nexa bold, Avenir\" size=36 align=\"center\" color=\"#00A89C\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-f38ee1d6-4fd7-4ff9-b304-84663f60fab2", - "position": { - "left": 651, - "top": 290, - "width": 76, - "height": 40, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Men's Shoes\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 0)'}\n| filterrows {getCell \"category\" | any {eq \"Men's Shoes\"}}\n| markdown {getCell \"percentage\"} font={font family=\"nexa bold, Avenir\" size=36 align=\"center\" color=\"#00A89C\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-0b0b1c4a-0acf-40e3-a3f0-3128bd59f45c", - "position": { - "left": 340, - "top": 293, - "width": 47, - "height": 29, - "angle": 0 - }, - "expression": "markdown \"%\" \n font={font family=\"nexa extrabold, Avenir\" size=24 align=\"left\" color=\"#00A89C\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-3efcd68f-7fca-4ad3-aa13-737260ec5436", - "position": { - "left": 525, - "top": 293, - "width": 47, - "height": 29, - "angle": 0 - }, - "expression": "markdown \"%\" \n font={font family=\"nexa extrabold, Avenir\" size=24 align=\"left\" color=\"#00A89C\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-a617cb84-6e50-47c3-bb95-ea67df89d117", - "position": { - "left": 710, - "top": 292, - "width": 47, - "height": 29, - "angle": 0 - }, - "expression": "markdown \"%\" \n font={font family=\"nexa extrabold, Avenir\" size=24 align=\"left\" color=\"#00A89C\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-5426b988-29d6-40aa-b0a2-40a4a0729ef0", - "position": { - "left": 277, - "top": 540, - "width": 93, - "height": 40, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Women's Accessories\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 0)'}\n| filterrows {getCell \"category\" | any {eq \"Women's Accessories\"}}\n| markdown {getCell \"percentage\"} \"\" font={font family=\"nexa bold, Avenir\" size=36 align=\"center\" color=\"#F05A24\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-c1579899-59fc-4960-9bf0-1187b3535593", - "position": { - "left": 461, - "top": 541, - "width": 93, - "height": 40, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Women's Clothing\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 0)'}\n| filterrows {getCell \"category\" | any {eq \"Women's Clothing\"}}\n| markdown {getCell \"percentage\"} font={font family=\"nexa bold, Avenir\" size=36 align=\"center\" color=\"#F05A24\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-31df9407-2156-408d-b9d4-54e32adfdc2b", - "position": { - "left": 644, - "top": 541, - "width": 93, - "height": 40, - "angle": 0 - }, - "expression": "essql 'SELECT category, COUNT(category) AS count FROM \"kibana_sample_data_ecommerce\" GROUP BY category'\n| mapColumn category fn={if {getCell category | eq \"Women's Shoes\"} then={getCell category} else=\"Other\"}\n| ply by=category fn={math 'sum(count)' | as count}\n| staticColumn total value={math 'sum(count)'}\n| mapColumn percentage fn={math 'round(count/total * 100, 0)'}\n| filterrows {getCell \"category\" | any {eq \"Women's Shoes\"}}\n| markdown {getCell \"percentage\"} font={font family=\"nexa bold, Avenir\" size=36 align=\"center\" color=\"#F05A24\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-d82b37e0-2f8e-48cb-8a85-d3d22b019499", - "position": { - "left": 343, - "top": 543, - "width": 45, - "height": 29, - "angle": 0 - }, - "expression": "markdown \"%\" \n font={font family=\"nexa extrabold, Avenir\" size=24 align=\"left\" color=\"#F05A24\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-ae570ea8-06b8-4ffa-a2f1-b7281481779f", - "position": { - "left": 528, - "top": 543, - "width": 45, - "height": 29, - "angle": 0 - }, - "expression": "markdown \"%\" \n font={font family=\"nexa extrabold, Avenir\" size=24 align=\"left\" color=\"#F05A24\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-81dd855d-d4fa-4c1c-a72f-6c6ab5b100cf", - "position": { - "left": 711, - "top": 543, - "width": 45, - "height": 29, - "angle": 0 - }, - "expression": "markdown \"%\" \n font={font family=\"nexa extrabold, Avenir\" size=24 align=\"left\" color=\"#F05A24\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-b8f13a87-b781-42d5-a663-5dbd4f645d6d", - "position": { - "left": 837, - "top": 468, - "width": 91, - "height": 63, - "angle": 0 - }, - "expression": "essql query=\"select COUNT(*) as total_count, SUM(CASE WHEN customer_gender='FEMALE' THEN 1 else 0 END) as female_count from kibana_sample_data_ecommerce\"\n| math \"round(100 * female_count / total_count)\" | markdown {context} font={font family=\"nexa bold, Avenir\" size=48 align=\"center\" color=\"#F05A24\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-86b06b67-893e-4555-ad38-7fba9ea3153b", - "position": { - "left": 837, - "top": 215, - "width": 90, - "height": 72, - "angle": 0 - }, - "expression": "essql query=\"select COUNT(*) as total_count, SUM(CASE WHEN customer_gender='MALE' THEN 1 else 0 END) as male_count from kibana_sample_data_ecommerce\"\n| math \"round(100 * male_count / total_count)\" | markdown {context} font={font family=\"nexa bold, Avenir\" size=48 align=\"center\" color=\"#00A89C\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-507337d9-6e0e-4752-8770-6ebe88e9b3da", - "position": { - "left": 913, - "top": 220, - "width": 44, - "height": 42, - "angle": 0 - }, - "expression": "markdown \"%\" \n font={font family=\"nexa extrabold, Avenir\" size=36 align=\"left\" color=\"#00A89C\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-19f6b029-a6ef-426e-aa07-8f91ef846a95", - "position": { - "left": 914, - "top": 475, - "width": 44, - "height": 42, - "angle": 0 - }, - "expression": "markdown \"%\" \n font={font family=\"nexa extrabold, Avenir\" size=36 align=\"left\" color=\"#F05A24\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-8ef60cfc-3823-42b6-9651-75a07b8e412d", - "position": { - "left": 19, - "top": 124, - "width": 44, - "height": 42, - "angle": 0 - }, - "expression": "markdown \"$\" \n font={font family=\"nexa bold, Avenir\" size=36 align=\"left\" color=\"#00A89C\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-3dfda897-cc59-4bf6-ad3d-38f00bd8814b", - "position": { - "left": 362, - "top": 606, - "width": 378, - "height": 92, - "angle": 0 - }, - "expression": "markdown \"ACME Inc.\" font={font family=\"gilroy extrabold, Avenir\" size=72 align=\"center\" color=\"#CDCDCD\" weight=\"bold\" underline=false italic=false}" - }, - { - "id": "element-b336534a-6604-41be-b34a-109d631dcdb2", - "position": { - "left": 27.25, - "top": 133.03796394394163, - "width": 195.5, - "height": 80.08297100325825, - "angle": 0 - }, - "expression": "essql \n query=\"SELECT sum(taxless_total_price) AS sum_total_price FROM \\\"kibana_sample_data_ecommerce\\\"\"\n| math \"sum_total_price\"\n| formatNumber \"0a\"\n| metric \n metricFont={font family=\"nexa bold, Avenir\" size=72 align=\"center\" color=\"#00A89C\" weight=\"bold\" underline=false italic=false}\n| render" - } - ] - } - ], - "colors": [ - "#37988d", - "#c19628", - "#b83c6f", - "#3f9939", - "#1785b0", - "#ca5f35", - "#45bdb0", - "#f2bc33", - "#e74b8b", - "#4fbf48", - "#1ea6dc", - "#fd7643", - "#72cec3", - "#f5cc5d", - "#ec77a8", - "#7acf74", - "#4cbce4", - "#fd986f", - "#a1ded7", - "#f8dd91", - "#f2a4c5", - "#a6dfa2", - "#86d2ed", - "#fdba9f", - "#000000", - "#444444", - "#777777", - "#BBBBBB", - "#FFFFFF", - "rgba(255,255,255,0)" - ], - "@timestamp": "2019-03-18T17:22:07.449Z", - "@created": "2019-03-18T16:05:37.945Z", - "assets": { - "asset-66a89124-fc39-4109-8acd-e1ac7f382e04": { - "id": "asset-66a89124-fc39-4109-8acd-e1ac7f382e04", - "@created": "2018-07-10T18:33:25.683Z", - "type": "dataurl", - "value": "data:image/png;base64,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" - }, - "asset-4acbecf1-e514-4e3b-bce4-61920335941e": { - "id": "asset-4acbecf1-e514-4e3b-bce4-61920335941e", - "@created": "2018-07-10T18:34:30.379Z", - "type": "dataurl", - "value": "data:image/png;base64,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" - }, - "asset-ae290f99-dd16-41a4-8191-7dd7be154be6": { - "id": "asset-ae290f99-dd16-41a4-8191-7dd7be154be6", - "@created": "2018-07-10T18:37:50.932Z", - "type": "dataurl", - "value": "data:image/png;base64,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" - }, - "asset-565cd264-9196-4ebd-9d6e-f413f1db734d": { - "id": "asset-565cd264-9196-4ebd-9d6e-f413f1db734d", - "@created": "2018-07-10T18:38:37.796Z", - "type": "dataurl", - "value": "data:image/png;base64,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" - }, - "asset-6222f3e0-1dab-4aa9-a06c-63d7732cc5f4": { - "id": "asset-6222f3e0-1dab-4aa9-a06c-63d7732cc5f4", - "@created": "2018-07-10T18:39:47.394Z", - "type": "dataurl", - "value": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADkAAAAcCAYAAADbeRcAAAAACXBIWXMAAAsSAAALEgHS3X78AAAD0ElEQVRYw92YMY/dRBSFv41WIqBI+2giBAVGcYWI4yKW6DIFPf4HOP/ANZV/gktK0yIK09Hh7ZDcGNeWeOmIhBRHAonONGfQPK+f37PXK20Y6cp+z57xnLnn3ntmLoZh4F1ofhCFwA5ourbul/S9uI8g/SDaAQlgZFfAK2APvNBr10AFFF1b798ZkAKXA98APwElUI1B6D0DxLISyI6BXQTSoYwna4C+a+tqA4AxUMg76SnvjABn8nzatXWxGKQfREYDxEAvytirBf3MrvzUR86YaCIPJl1blysXycijN4AeBalOuUDkArCfWc0YSPV+eu5kHYCma+tmg+RUjYFOgvSDKNOEs66t85W0K7u2Tk68aykfb0H5kUdD65QHEy8V8opZChBAHvSAUGPNtUzZsdoqeWmsUmNzA6QfRLni7FbUUR0zAprPeDF2J7NhyzT2IUgnwSRLi+0M0BhINPa4xaJ0vzVC0XRvv3s5Qp/dNvjHH5MnUyUEt9nY2brWGoULwHd+EP186WSlsGtrcwfUyYE3fhDtRl7bqQytAWLrtNE4oewK+E0lzlpx6VCnuAOAdG3d+0F0vdRzKkuhIzyMU5PfKis3ApJJlDSjCkHX1s2lQ52cu2uVJlzOqCjjAHK9sheYwt6fGcfGOs6Nyf6WsWAnO24e8BXwyA+iL4GH+u8x8Avwj+xvXf+SXclrz4CvnfHe+kGUnqGsrDDg4snT51b3VU6MHJuwpctUewX8KRCPdH3PGacHXgN/yB4DnwEvJ8bqjyVAiY1cc82maqxUVNq1dTj25EPnvpxJCo0T6C7NPtXzPfCjQDX6XQD5WOr5QbR3CvgSsVEq5ko/iA52II5gzw5knTrsurZOz0gGlQPWxst+ZuV3wBvgw3EsacUzZfZ+RYhYXW29a81zK4UFGaowe2eI35OadEIHh11bx0eel/JQfMt8kANfAO8Dn7iL9sCmWSmEdKbAVqLFEoCehMBc5k4Azw+iSp5ZU6Ysqz5QeSkF/IZ2zYDMfehQyu7TlpaZ8pQAd3SulWLJQi8axbYHfC6rgMoPosIPot3BVkvUsjuQXpIsWSPYtQOxYr8/s0/seD3X0UdzhJ5GLEEMKyZYlAPejf2ks9X6FfhIgr1ZSFGbRc3KhGLPboyytj3E8pzfNj+cVFFTIHdAC3wM/AB8e855i/qlsuJUpl4pNPo1G4gxXUPVtMapNfbkrLJ6UVR2hbGRKvle9XCzncwW7T+QTok48IJzfmOL/gun/7WKfjV3BnQvQCqOfgderjltu/dtGAaGYeDJ0+eevf+/2b9/hrMMA+Zz7QAAAABJRU5ErkJggg==" - }, - "asset-f8ac482e-0c7a-4cac-8bcf-6f4f55300081": { - "id": "asset-f8ac482e-0c7a-4cac-8bcf-6f4f55300081", - "@created": "2018-07-10T18:40:34.480Z", - "type": "dataurl", - "value": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAD8AAAAZCAYAAACGqvb0AAAACXBIWXMAAAsSAAALEgHS3X78AAADxklEQVRYw8WYz4rkVBTGf92IoCATBFEEMVK1Eqoqi6p1Z+HOhXmDrkeo2boxs5ql8Q3iG9QguE6ju4DGrEaIkNm5EUpwbWbz3eH27Zt00nbFA6GKqntzznf+n3PRdR1DtFzvQiAGzCdAAGyco38Dlb63ehLPuYfS95LB0NUI/kVTl0XfCy/6wC/Xuz1wkPA3QGG9FKBq6vJknY+kFIDIUtYGeAEcm7rMx6CUwvd6AoGawt82VgAcgbSpy3YQ/HK9M4dDIJXQp4eaS+9L9C6AfZ81dPYAfCNL50OWm6DIFLgGnjZ1mXnBi3khre55ZJI3ZcDB9QKL90kKah+ZdySj5k1dpj7wORA0dZlwJpIQhQAerd8LoD2H0j28o6Yu20tH89dyu7NRU5eVeGQWbxOjc/DOTAheWv8lwM1ju1uPELllCZTYsv+SWybQUVhvgQ/lEnNRpapgeFdzMJX1nyzXu9AGH1tlZA46WaVpbroBboFnZvCutWdXxOX/oXYl1ysLfGHicFbwy/UuULYNgEiCnZsS4IWV4HIgOTdvF+vFYrWNlP43wF/AwrnzyhMOhSdcWiexFANCtG6npzpfmAbEcyf2hE3o/OaeiYAnnnjfAK/eNDlinrpCOz2zic3IwyRwBPvUOfMH8A/wMfCelPqn/vtI9z8AfgfeBz507v+mJGknzMpTQewzra90G6xvOdaLXKuqNNyplWpVY0f7vmnPeE4AvCOAPwD/Wgp7KaG/BD6RAl46lttYljO8TIU62t3iCAoAfODHJKtcZzN9v9fdda9yh4ue/v7npi4PPf/7vC5Xa56ObJQ2TV0WtttH0mA4ADxWh2R689OE7D5qYLL678OEEdieRJMebzVnEykpunTd25NYzKVMDA5NXSbnAG7JEQOZQmtM13Zq6jKWJ/66XO/SeypN7pvqUiDWi2xLGAskU3p/6+7kEdnygOOUuxbPO6OxZvsKCJu6PLlNTgaERuPL9e5gCRBNBJ48RHjHAyL1HoUEZ4LnVEAlObD6iTcDlG+TE2vt9Avw2dDmZWBzkkmA/cQs3Bc2qSa/tC9ZDhggV7i+DXze1GU0tMaKgR9Vlr5u6vL5xL3bwcoNjzaiSi4TfumEZLhWeXwX+KKpy5/ugHc0vFfpy5RBM3VfVc9mNwG+0t4tPedOQCGZWrP50dOYBZZc18B3wvMt8OzWGksgjj1JIpYyYnVtZkV8ZX03u7E5lhF2YjNybSRL6zRDRjmtJxkmdF3HYrUNFqvtoes67nsWq224WG3jMWfnfMbKJaz7rut4DW0holiCTPaYAAAAAElFTkSuQmCC" - }, - "asset-2f64bd10-953d-4163-90e9-a55e9ca4c52a": { - "id": "asset-2f64bd10-953d-4163-90e9-a55e9ca4c52a", - "@created": "2018-07-13T13:12:49.516Z", - "type": "dataurl", - "value": "data:image/png;base64,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" - }, - "asset-3a26727a-b756-44be-a82c-273dd85bda09": { - "id": "asset-3a26727a-b756-44be-a82c-273dd85bda09", - "@created": "2018-07-13T13:12:56.611Z", - "type": "dataurl", - "value": "data:image/png;base64,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" - }, - "asset-93e415d8-82c6-47d4-8c55-e38df329b88b": { - "id": "asset-93e415d8-82c6-47d4-8c55-e38df329b88b", - "@created": "2018-07-13T13:13:02.894Z", - "type": "dataurl", - "value": "data:image/png;base64,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" - }, - "asset-aaa14d64-2c1c-47f2-95c0-21306ee18cba": { - "id": "asset-aaa14d64-2c1c-47f2-95c0-21306ee18cba", - "@created": "2018-07-13T13:14:32.348Z", - "type": "dataurl", - "value": "data:image/png;base64,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" - }, - "asset-960c8c6e-da72-412d-9d04-34a98cdb5760": { - "id": "asset-960c8c6e-da72-412d-9d04-34a98cdb5760", - "@created": "2018-07-13T13:14:42.533Z", - "type": "dataurl", - "value": "data:image/png;base64,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" - }, - "asset-8ae4b612-43a3-4846-8f0d-abb9785e95c3": { - "id": "asset-8ae4b612-43a3-4846-8f0d-abb9785e95c3", - "@created": "2018-07-13T13:15:19.432Z", - "type": "dataurl", - "value": "data:image/png;base64,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" - }, - "asset-b1602228-f014-402d-88cd-42821af09dcf": { - "id": "asset-b1602228-f014-402d-88cd-42821af09dcf", - "@created": "2018-08-21T18:29:08.616Z", - "type": "dataurl", - "value": "data:image/png;base64,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" - }, - "asset-18070a2a-cd01-410a-ba89-a4505e2fbc5b": { - "id": "asset-18070a2a-cd01-410a-ba89-a4505e2fbc5b", - "@created": "2018-09-06T19:41:55.368Z", - "type": "dataurl", - "value": "data:image/png;base64,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" - }, - "asset-803ec373-2608-4f6f-8cf9-0dbb2f6437ce": { - "id": "asset-803ec373-2608-4f6f-8cf9-0dbb2f6437ce", - "@created": "2018-09-06T19:42:19.366Z", - "type": "dataurl", - "value": "data:image/png;base64,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" - }, - "asset-466fdcbd-f265-4081-bbcc-367bfcfeaf4f": { - "id": "asset-466fdcbd-f265-4081-bbcc-367bfcfeaf4f", - "@created": "2018-09-06T19:43:02.342Z", - "type": "dataurl", - "value": "data:image/png;base64,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CiRdClkAAAAcaURPVAAAAAIAAAAAAAACyQAAACgAAALJAAACyQACUiJfkBKEAABAAElEQVR4AezdCZydWV0n/H9XUvuayr6vvdB729isza6AiDJuw7jMAOoroKMMigqDA6g4KoPOi7ijiIoivDqoL7I1Co3QG73vnU46+1qpVFJVqSXbnCdNQpKuSm7VfZ6qu3wPn/rUvc9ynnO+5zZPbt3fPeeS5z73uSdDIUCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBHIVuCQL6N1+++25VqoyAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBQ7wKXJAAz6NX7q0D/CRAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQCB3gTMBvdGBp3KvXIUECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQKAeBVp61oaAXj2OvD4TIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQKECAnqF8qqcAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBOpVQECvXkdevwkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgUAEBvUJ5VU6AAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEC9SogoFevI6/fBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIFCogIBeobwqJ0CAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIF6FRDQq9eR128CBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQKFRAQK9QXpUTIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQL0KCOjV68jrNwECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgUKiCgVyivygkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgXgUE9Op15PWbAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAoVENArlFflBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIFCvAgJ69Try+k2AAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEChQoI6BXKq3ICBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQqFcBAb16HXn9JkCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIFCBQT0CuVVOQECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAjUq4CAXr2OvH4TIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQKECAnqF8qqcAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBOpVQECvXkdevwkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgUAEBvUJ5VU6AAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEC9SogoFevI6/fBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIFCogIBeobwqJ0CAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIF6FRDQq9eR128CBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQKFRAQK9QXpUTIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQL0KCOjV68jrNwECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgUKiCgVyivygkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgXgUE9Op15PWbAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAoVENArlFflBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIFCvAgJ69Try+k2AAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEChQoI6BXKq3ICBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQqFcBAb16HXn9JkCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIFCBQT0CuVVOQECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAjUq4CAXr2OvH4TIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQKECAnqF8qqcAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBOpVQECvXkdevwkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgUAEBvUJ5VU6AAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEC9SogoFevI6/fBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIFCogIBeobwqJ0CAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIF6FRDQq9eR128CBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQKFRAQK9QXpUTIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQL0KCOjV68jrNwECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgUKiCgVyivygkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgXgUE9Op15PWbAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAoVENArlFflBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIFCvAgJ69Try+k2AAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEChQoI6BXKq3ICBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQqFcBAb16HXn9JkCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIFCBQT0CuVVOQECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAjUq4CAXr2OvH4TIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQKECAnqF8qqcAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBOpVQECvXkdevwkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgUAEBvUJ5VU6AAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEC9SogoFevI6/fBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIFCogIBeobwqJ0CAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIF6FRDQq9eR128CBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQKFRAQK9QXpUTIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQL0KCOjV68jrNwECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgUKiCgVyivygkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgXgUE9Op15PWbAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAoVENArlFflBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIFCvAgJ69Try+k2AAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEChQoI6BXKq3ICBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQqFcBAb16HXn9JkCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIFCBQT0CuVVOQECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAjUq4CAXr2OvH4TIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQKECAnqF8qqcAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBOpVQECvXkdevwkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgUAEBvUJ5VU6AAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEC9SogoFevI6/fBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIFCogIBeobwqJ0CAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIF6FRDQq9eR128CBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQKFRAQK9QXpUTIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQL0KCOjV68jrNwECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgUKiCgVyivygkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgXgUE9Op15PWbAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAoVENArlFflBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIFCvAgJ69Try+k2AAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEChQoI6BXKq3ICBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQqFcBAb16HXn9JkCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIFCBQT0CuVVOQECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAjUq4CAXr2OvH7XvMD49k1x4sjQtPrZuHBpzOldNKVzR+77Whw7sDfGn3jgzHlNl10bc+cvjtbrX3BmWykPZqrtJ0aGY3zbkxM2qeXy6ybcfrGNE9U5Hc+LXcd+AgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgACByhcQ0Kv8MdJCAtMSOPCH742jmx6b1rntr/6h6Hz591303CyMNvz1z8fIv/5TnBgbnfT4huaWaH3Z95RUZ1bJTLQ9u07W9sF/+Gj28Bll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ObtLWeZ2ooDeOZWc9yRrw/y3vPe8rZ4SIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAjUgoCAXi2Moj4QKFGg3JDb6ctky9b2/+F7nxFgO73/Qr+zQNq8N7yj5KVtT9dVTtsnWpr3YkvWTuecrK0CeqdHzG8CBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEBPa8BAnUkUE7I7XymLKQ3dMvfx5Gv/Mv5uyZ93v7qH4rOl3/fpPsvtKOctk90bimz4U00697FlrkV0LvQKNpHgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIEKgvAQG9+hpvva1zgYmCatNZ4vZsxiyQNnr/12L0zlvP3nzmcUNzSzRdd1N0vuIHYk7vojPbp/qgnLZPJ2iXtW+ia14s2CegN9WRdTwBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAoHYFBPRqd2z1jMCMC2ThtJMjQ3HswN5oXLE+Gto6omnl+hlvhwsSIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQqAQBAb1KGAVtIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIGaExDQq7kh1SECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQqAQBAb1KGAVtIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIGaExDQq7kh1SECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQqAQBAb1KGAVtIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIGaExDQq7kh1SECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQqAQBAb1KGAVtIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIGaExDQq7kh1SECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQqAQBAb1KGAVtIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIGaExDQq7kh1SECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQqAQBAb1KGAVtIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIGaExDQq7kh1SECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQqAQBAb1KGAVtIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIGaExDQq7kh1SECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQqAQBAb1KGAVtIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIGaExDQq7kh1SECMy8wtvGhmb9ozldsvvTqnGtUHQECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQL0LCOjV+ytA/wnkILDnF16fQy2zW0Xby14bXd/1I7PbCFcnQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBCoKQEBvZoaTp0hMDsCAnqz4+6qBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEClS0goFfZ46N1BKpCQECvKoZJIwkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBGZYQEBvhsFdjkAtCgjo1eKo6hMBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEC5AgJ65Qo6nwCBENDzIiBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECDwTAEBvWea2EKAwBQFDv//fzXFMyrv8K7v/rHKa5QWESBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVLWAgF5VD5/GEyBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEClCgjoVerIaBcBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVLWAgF5VD5/GEyBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEClCgjoVerIaBcBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVLWAgF5VD5/GEyBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEClCgjoVerIaBcBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVLWAgF5VD5/GEyBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEClCgjoVerIaBcBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVLWAgF5VD5/GEyBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEClCgjoVerIaBcBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVLWAgF5VD5/GEyBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEClCgjoVerIaBcBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVLWAgF5VD5/GEyBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEClCgjoVerIaBcBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVLWAgF5VD5/GEyBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEClCgjoVerIaBcBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVLWAgF5VD5/GEyBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEClCgjoVerIaBcBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVLWAgF5VD5/GEyBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEClCgjoVerIaBcBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVLWAgF5VD5/GEyBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEClCgjoVerIaBcBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVLWAgF5VD5/GEyBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEClCgjoVerIaBcBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVLWAgF5VD5/GEyBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEClCgjoVerIaBcBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVLWAgF5VD5/GEyBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEClCgjoVerIaBcBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVLWAgF5VD5/GEyBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEClCgjoVerIaBcBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVLWAgF5VD5/GEyBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEA9CowfPR4HD4/Fvv4jp7q/ckln9HQ21yOFPhMgQIAAgYoWENCr6OHROAIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAg8C2Bw0Nj0Z+CeYPD49/a+M1H11y64BnbbCBAgAABAgRmV0BAb3b9XZ0AAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECJQk8ODGvgset3pZZ3S1m0Xvgkh2EiBAgACBGRYQ0JthcJcjQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQKlCJw4cTIaGi6JiwXzTtfV0dYYa5d3n37qNwECBAgQIFABAgJ6FTAImkCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBM4W2N03HH0HR87eVNLjdcu7or2tqaRjHUSAAAECBAgULyCgV7yxKxAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgZIEDgyMxK79wyUdO9FBXR3NsXpp50S7bCNAgAABAgRmQUBAbxbQXZIAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECJwtcGT0aOxOwbwjo8fO3jytxxtWdkdrS+O0znUSAQIECBAgkK+AgF6+nmojQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQJTEshmzMtmzsurmEUvL0n1ECBAgACB8gUE9Mo3VAMBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIEJiywKHBsdi2Z3DK55VywrrlXdHe1lTKoY4hQIAAAQIEChQQ0CsQV9UECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQOB8gRMnTkY2a97Bw6Pn78rteUdbY6xd3p1bfSoiQIAAAQIEpicgoDc9N2cRIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIEpCwwOj6dw3lCMHz0x5XOnesKaZV3R2W4Wvam6OZ4AAQIECOQpIKCXp6a6CBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIDAJAJ7DxyJff1HJtmb/+b21rmxbkVP/hWrkQABAgQIEChZQECvZCoHEiBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBqQscO3Yitu8djKEjR6d+cplnrFrSGd2dzWXW4nQCBAgQIEBgugICetOVcx4BAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIELiIwFBa0nbHvqE4mkJ6s1Fam+fEhlXzZuPSrkmAAAECBAgkAQE9LwMCBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIFCAQN/BkdjdN1xAzVOrcsXijpjX1TK1kxxNgAABAgQI5CIgoJcLo0oIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgMC3BHamWfP6D41+a8MsPmpumhOXrTaL3iwOgUsTIECAQB0LCOjV8eDrOgECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAjkK3AsLWW7fe9gDB05mm/FZda2bGF7zO9pLbMWpxMgQIAAAQJTFRDQm6qY4wkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAwAQCI6NH48nthybYM/ubGuc2xBVre2e/IVpAgAABAgTqTEBAr84GXHcJECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAIH+Bw0NjsXX3YP4V51jjkgXtsXCeWfRyJFUVAQIECBC4qICA3kWJHECAAAECBAgQIECAAAECBAgQIECAAAEC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AQQQQAABBBBAAAEEEEAAAQQQQAABBBCISwEC9OLysnPSCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACTgsQoOe0MO0jgAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgjEpQABenF52TlpBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABpwUI0HNamPYRQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQTiUoAAvbi87Jw0AggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIICA0wIE6DktTPsIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAJxKUCAXlxedk4aAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEDAaQEC9JwWpn0EEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAIG4FCBALy4vOyeNAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCDgtAABek4L0z4CCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEBcChCgF5eXnZNGAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBwWoAAPaeFaR8BBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQCAuBQjQi8vLzkkjgAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgg4LUCAntPCtI8AAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIBCXAgToxeVl56QRQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQScFiBAz2lh2kcAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEIhLAQL04vKyc9IIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAJOCxCg57Qw7SOAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCMSlAAF6cXnZOWkEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAGnBQjQc1qY9hFAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBOJSgAC9uLzsnDQCCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggIDTAgToOS1M+wgggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAnEpQIBeXF52ThoBBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQMBpAQL0nBamfQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAgbgUIEAvLi87J40AAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIOC0AAF6TgvTPgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAQFwKEKAXl5edk0YAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEHBagAA9p4VpHwEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAIC4FCNCLy8vOSSOAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCDgtQICe08K0jwACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgggEJcCBOjF5WXnpBFAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBJwWIEDPaWHaRwABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQiEsBAvTi8rJz0ggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAII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gAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAQCUCBOhVokVZBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBMoUIECvTCiKIYAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIFCJAAF6lWhRFgEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAIEyBQjQKxOKYggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAghUIkCAXiValEUAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEECgTAEC9MqEohgCCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAAClQgQoFeJFmURQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQKFOAAL0yoSiGAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAQCUCBOhVokVZBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBMoUIECvTCiKIYAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIFCJAAF6lWhRFgEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAIEyBQjQKxOKYggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAghUIkCAXiValEUAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEECgTAEC9MqEohgCCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAAClQgQoFeJFmURQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQKFOAAL0yoSiGAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAQCUCBOhVokVZBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBMoUIECvTCiKIYAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIFCJAAF6lWhRFgEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAIEyBQjQKxOKYggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAghUIkCAXiValEUAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEECgTAEC9MqEohgCCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAAClQgQoFeJFmURQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQKFOAAL0yoSiGAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAQCUCBOhVokVZBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBMoUIECvTCiKIYAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIFCJAAF6lWhRFgEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAIEyBQjQKxOKYggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAghUIkCAXiValEUAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEECgTAEC9MqEohgCCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAAClQgQoFeJFmURQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQKFOAAL0yoSiGAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAQCUCBOhVokVZBBAwTeDmjVtMq9sOFb9z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ktko/+S1mPmXdWwwoODuF6HLzJbtjndj0aR7xNK+f8U7bF289CFiuduRXO62Dgh3kpJAyTtPiWVtlyZsbua+A2E9/6aExfcFdqxZjNIpz/gO47bNufFhmNp3iVs8BiIBEogNAQr0YsOVXkmABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEggJgQcq35B6TvPx8R3KKeWEacjc8TZoUzi2ufevQMlz90Dj7MyrnH9g5kPPwbWUVf5N3GfBJKGQMlbT8D1+4qEzcfQqh3ybnoMMBgTloN/4JLXHoZr/Rr/prjs593+NAz5beISi0FIgARiQ4ACvdhwpVcSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESiDoB5+9LYHvr6aj7DedQ1yJHVM+bHM4s7v2O1YtQ+vazcY/rH9DcbzCs517v38R9EtA0gdoqJ/a8NQGuv/5I6Dxyb3oExnadE5qDf3DX5vUomXSff1Pc9vPveg76nJZxi8dAJEAC0SVAgV50edIbCZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACcSEgGPVQlE577mY+A7nNOvsq5He/+hwZgnpL581FfbvPk1IbF9QU8++yLl4HKDT+Zq4JQFNEqixFWHP20/CvXVzQvPPOvcapPcbmtAc5ILbpr4C58K5cl0xb8u7/SlRSa9tzOMwAAmQQPQJUKAXfab0SAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAJRJWBfPBvln74WVZ9KnZm69UTOVeOVmifEzvbhJDiXz09IbF9QQ8cuaHHRWOizrL4mbklAUwSqt/2NPe8+A0/x7oTmnT58JLKOPzehOQQL7qksR9FjN8HjsAcziWl7zvUPwrRft5jGoHMSIIHoE6BAL/pM6ZEESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEokagfNZnokLcZ1HzF6mj3JsfhbFtp0iHxd2++MXxqN60Ie5x/QPq81vCesGNmuDlnzf3ScC5djnK3nsBHrG8bSJf5r6Hw3r+jYlMIWxs+y/fo/zzt8Laxcog++KbkHbQwFi5p18SIIEYEKBALwZQ6ZIESIAESIAESIAESIAESIAESIAESIAESIAE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GI778TeEJz76ofBvvvvv0jEnnBRWn3RaiP8HJz8ECBAgQIAAAQIECBAg0L+AgF7/xlogQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgQEKCOgNcNINmQABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgT6FxDQ699YCwQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECAwQAEBvQFOuiETIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQP8CAnr9G2uBAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAYoIKA3wEk3ZAIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBDoX0BAr39jLRAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIDAAAUE9AY46YZMgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAv0LCOj1b6wFAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIEBiggIDeACfdkAkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgfwEBvf6NtUCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECAxQQ0BvgpBsyAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECPQvIKDXv7EWCBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQGCAAgJ6A5x0QyZAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgACB/gUE9Po31gIBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIDFBAQG+Ak27IBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQINC/gIBe/8ZaIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIEBCgjoDXDSDZkAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIE+hcQ0OvfWAsECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgMEABAb0BTrohEyBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgED/AgJ6/RtrgQABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQGKCCgN8BJN2QCBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQ6F9AQK9/Yy0QIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAwAAFBPQGOOmGTGASBX78r26ZxG7p05QJfPRHL5yyHusuAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgMMsCAnqzPLvGRmCKBAT0pmiyJrirAnoTPDm6RoAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEBiggoDfASTdkApMoIKA3ibMyfX0S0Ju+OdNjAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECAwywICerM8u8ZGYIoEBPSmaLImuKsCehM8ObpGgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQGKCCgN8BJN2QCkyggoDeJszJ9fRLQm74502MCBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIDDLAgJ6szy7xkZgigQE9KZosia4qwJ6Ezw5ukaAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAYoIKA3wEmf9CEf3rc3HPj6/QvdXP3s08OKtesX9osb+7/yxZFDK9Y9I6zesnXkWJudfV+4PRza8Vg48E9fWii2+nlnh5XHPyusfdEFC8eabhT7F8vVjSle07VcLDuNPwJ60zhrk9dnAb3JmxM9IkCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECQxYQ0Bvy7E/o2GMwbdcHfmOhdytP3hJO+KXrFvaLG4/+8k+OHFq19Yxw/FuuGTlWtxNDgXs/fXPY98mPhcPf3l96+Ypj14S1r/yxsOHCi0uvKZ7YecN14cA9d445lQBHAABAAElEQVQcPu61P18Z9jvw4Law871XjZRZsWkubH77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AAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECAxcQ0Bv4DTCJw28S0Ithucff/d+Sj7ptEtCL406tZBePx7Dc2pf+UFh16ta4e+Tn4EPbwr7P/O2iEF082SVIt/3dlyfr+k5r3/nvze/6YOPQX77ctG4L6E3rzE1WvwX0Jms+9IYAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECAxdQEBv6HfABI6/SUAvdjt1XTzeNKAXr338t68Mhx55MG52+qlaca+qwt03/XF48taPl17S9HG7pRVM4QkBvSmctAnssoDeBE6KLhEgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgQELCOgNePIndeip4N36V10aNlx48aIup4JubQJ6cSW+nddf0ymkF9t55mVXdlrlLvWY2/zguqzKly8/jdsCetM4a5PXZwG9yZsTPSJAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAkMWENAb8uxP6NjbBPTiEIqr4LUJ6MXyMaT3L3/7l5Ur2sXr8j9lgcH8NXXbVY+5HdrjbaOVgF7dHeN8EwEBvSZKriFAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgSWS0BAb7mktdNYoG1AL65Gt+v97wqHv73/SBttA3pZx2K7+794e9h/x23ZoZHXFceuCavPOT9s+KGfCMfMbR4512UntfpfrGeIj7eN4xbQiwp+liogoLdUQeUJECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBcQoI6I1TU10zIxDDev+671/CoR2PhVWnbg0r1j0jrN6ydWbGN4kDEdCbxFmZvj4J6E3fnOkxAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBCYZQEBvVmeXWMjMEUCf/foN6eot7o6qQI/+D0nTmrX9IsAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAYoICA3gAn3ZAJECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAoH8BAb3+jbVAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgMUENAb4KQbMgECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAj0LyCg17+xFggQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIEBggAICegOcdEMmQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgf4FBPT6N9YCAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECAxQQEBvgJNuyAQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECDQv4CAXv/GWiBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBAQoI6A1w0g2ZAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBPoXENDr31gLBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIDBAAQG9AU66IRMgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIBA/wICev0ba4EAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEBiggoDfASTdkAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIEOhfQECvf2MtECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgMAABQT0BjjphkyAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEC/QsI6PVvrAUCBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQGKCAgN4AJ92QCRAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQKB/AQG9/o21QIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIDFBDQG+CkGzIBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQI9C8goNe/sRYIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAYIACAnoDnHRDJkCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIH+BQT0+jfWAgECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgMUEBAb4CTbsgECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAg0L+AgF7/xlogQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgQEKCOgNcNINmQABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgT6FxDQ699YCwQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECAwQAEBvQFOuiETIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQP8CAnr9G2uBAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAYoIKA3wEk3ZAIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBDoX0BAr39jLRAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIDAAAUE9AY46YZMgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAv0LCOj1b6wFAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIEBiggIDeACfdkAkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgfwEBvf6NtUCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECAxQQ0BvgpBsyAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECPQvIKDXv7EWCBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQGCAAgJ6A5x0QyZAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgACB/gUE9Po31gIBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIDFBAQG+Ak27IBCZR4Mf/6pZJ7JY+EWgt8NEfvbB1GQUIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgACB2RQQ0JvNeTUqAlMnIKA3dVOmwyUCAnol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CBAgQIAAAQIECBAgQIAAAQIEOgoI6HWEU4wAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECFQJCOhV6ThHgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQ6CgjodYRTjAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVAkI6FXpOEeAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBDoKCOh1hFOMAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAhUCQjoVek4R4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEOgoI6HWEU4wAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECFQJCOhV6ThHgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQ6CgjodYRTjAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVAkI6FXpOEeAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBDoKCOh1hFOMAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAhUCQjoVek4R4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEOgoI6HWEU4wAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECFQJCOhV6ThHgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQ6CgjodYRTjAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVAkI6FXpOEeAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBDoKCOh1hFOMAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAhUCQjoVek4R4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEOgoI6HWEU4wAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECFQJCOhV6ThHgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQ6CgjodYRTjAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVAkI6FXpOEeAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBDoKCOh1hFOMAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAhUCQjoVek4R4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEOgoI6HWEU4wAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECFQJCOhV6ThHgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQ6CgjodYRTjAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVAkI6FXpOEeAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBDoKCOh1hFOMAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAhUCQjoVek4R4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEOgoI6HWEU4wAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECFQJCOhV6ThHgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQ6CgjodYRTjAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVAkI6FXpOEeAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBDoKCOh1hFOMAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAhUCQjoVek4R4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEOgoI6HWEU4wAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECFQJCOhV6ThHgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQ6CgjodYRTjAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVAkI6FXpOEeAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBDoKCOh1hFOMAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAhUCQjoVek4R4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEOgoI6HWEU4wAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECFQJCOhV6ThHgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQ6CgjodYRTjAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVAkI6FXpOEeAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBDoKCOh1hFOMAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAhUCQjoVek4R4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEOgoI6HWEU4wAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECFQJCOhV6ThHgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQ6CgjodYRTjAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVAkI6FXpOEeAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBDoKCOh1hFOMAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAhUCQjoVek4R4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEOgoI6HWEU4wAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECFQJCOhV6ThHgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQ6CgjodYRTjAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVAkI6FXpOEeAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBDoKCOh1hFOMAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAhUCQjoVek4R4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEOgoI6HWEU4wAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECFQJCOhV6ThHgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQ6CgjodYRTjAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVAkI6FXpOEeAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBDoKCOh1hFOMAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAhUCQjoVek4R4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEOgoI6HWEU4wAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECFQJCOhV6ThHgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQ6CgjodYRTjAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVAkI6FXpOEeAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBDoKCOh1hFOMAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAhUCQjoVek4R4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEOgoI6HWEU4wAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECFQJCOhV6ThHgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQ6CgjodYRTjAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVAkI6FXpOEeAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBDoKCOh1hFOMAAECBAgQIECAqH0IJAAAAVFJREFUAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAhUCQjoVek4R4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEOgoI6HWEU4wAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECFQJCOhV6ThHgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQ6CgjodYRTjAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVAkI6FXpOEeAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBDoKCOh1hFOMAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAhUCcSA3v8HLMnJsGUNNCMAAAAASUVORK5CYII=" - }, - "asset-e644a484-4097-40b9-a08e-7250ba963059": { - "id": "asset-e644a484-4097-40b9-a08e-7250ba963059", - "@created": "2018-09-06T19:44:43.075Z", - "type": "dataurl", - "value": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABDgAAAQ4CAYAAADsEGyPAAAACXBIWXMAAAsSAAALEgHS3X78AAAgAElEQVR4nOzdTXIbZ7Yu6nVOnP7OO4INR2S/pBEYGkFR3ewUOQJTI5A0AlEjINxBV6wRCB6BUX1EGHsEN88I7m3kRwuS+AOAQH75ZT5PhMNV3vWzYpdJJN5cP/8rAIBJaZt6FhGznb/04z+PiPhHRFQP/NtfPfLXz20dEe0Df30bEf/z1L+2Wm5WZ6sKABiM/5W7AADgZdqm3g0d5unP/xVdGBE//PWp26Y/IroQ5D87/3id/vG6Wm4eClMAgAETcADAgLVNPU//8P7P950VVXwfYHAeq/TndUT83/gWkLTVcrN++N8CAOQg4ACAjH4IMO67LoQX5bjv/LjvBrn/59tqudlmrAsAJkfAAQBnlkKM+9Div6Pbd5FrlwX92qY/1tHtChF+AMCZCDgA4ERSkDFLf/waDy/vhHvr6MKP/8S34MPYCwAcScABAAdKSz1fhSCD89gNPlah4wMA9iLgAIBHtE19P1byKrrlnvf/GPp2v9tjHanjQ7cHAHxPwAEA8V2YMY9vYcYsY0mwj1UIPQAgIgQcAExUGjOZh84MxmcVXejxR3ShxzZrNQDQEwEHAKOXujPm0YUYv6Z/DFOxje8Dj1XWagDgTAQcAIxO29Sz6EKM+zBjlq8aGKRVpMAjIlbVctPmLQcAXk7AAUDxdsZNfg27M+AY6/gWegg8ACiSgAOA4ux0aPwz/bnKWA6MkcADgOIIOAAYvLRD4yKMnEAu64j4d3RhxypzLQDwIAEHAIPUNvU8vnVouHACw3IXXXfHnSstAAyFgAOAQUhjJ7tdGsZOoAzb6MZZ/l0tN3d5SwFgygQcAGSjSwNGSXcHAFkIOADozQ+7NC5ClwaM3f2y0t+r5WaduRYARk7AAcBZ/TB6cpG3GiCjNrruDqMsAJyFgAOAk9sJNf4VRk+An7WR9nZEN8riDC0ALybgAOAk2qZ+Fd0uDaEGcKi7EHYA8EICDgCOljo1LqMLNWY5awFGQ9gBwFEEHAAcxPgJ0CNhBwB7E3AA8Kx0/eQyhBpAHhaUAvAsAQcAD9o56frPcP0EGI77sOOz07MA7BJwAPCdtqnn0XVqXERElbcagCdtI+L3iFhUy802bykA5CbgAOB+r8Zv0YUas6zFABxnFV3YYV8HwEQJOAAmamcE5V/RnXcFGIP7EZbfq+VmlbkWAHok4ACYmLapX8W3bg0jKMCYbSPic3QjLLo6AEZOwAEwATvdGr+FKyjANC1CVwfAqAk4AEZMtwbAT7ahqwNglAQcACOjWwNgb4vQ1QEwGgIOgJHYuYRyGbo1AA6xjYiP4QILQNEEHACFa5v6vltjnrkUgNK10XV1fK6Wm23eUgA4lIADoEBpDOUyumBjlrUYgHG6iy7oWOUuBID9CDgACmIMBaB324j4WC03i8x1APAMAQdAAdqmnkfEv6ILNgDoXxvd9ZUbezoAhknAATBgbVNfRhdszPNWAsCORXRdHdvMdQCwQ8ABMDA7Z17fh/0aAENmTwfAgAg4AAYiBRvX0e3YsF8DoByr6IKOu9yFAEyZgAMgs7Q49DIEGwCl24aFpADZCDgAMknBxvuwOBRgbLYh6ADonYADoGeCDYDJ2EZ3eWXh8grA+Qk4AHoi2ACYLCdmAXog4AA4M8EGAImgA+CMBBwAZyLYAOARgg6AMxBwAJyYYAOAPbXRnZf9kLsQgDEQcACciGADgCNtw9UVgBcTcAC8UNvUVXTBxnXuWgAo2jYEHQBHE3AAHCkFG9cR8VtEVJnLAWA8thHxrlpu7nIXAlASAQfAEdqm/hCCDQDOaxVdR8cqcx0ARRBwABygberL6MZRZnkrAWBCVhFxVS0328x1AAyagANgD21TzyPiU0S8ylwKANO1iG50xWlZgAcIOACekC6j3EbEPG8lABAR6bRsRNwIOgC+J+AAeEBaIPopnHwFYJi24eIKwHcEHAA/sEAUgIKsoxtbWeUuBCA3AQdA0jb1RXRdG7PMpQDAoe6iCzq2uQsByEXAAUxe29Svogs25plLgSFaPfDX2oj4T481/PrAX6vC0l94yMewnwOYKAEHMFlpz8b7iLjOXQucWRtdG3tEN7f/Pw/89YiIbclvf9O1o3s/BiC7Icnuvw7GaBv2cwATJOAAJqlt6svoujbs2aBkuwHFH+nP2/RHmMl/Wgo570OQV9H9Pvjv6MbUdIgwBqvoxlbWz/0LAcZAwAFMSnrD+yl8caEcq/g2EnIfaLS+sPTjhxBkHhH/Fd/CEL9HKMVNdB0dxlaAURNwAJPg7CsDt4pvIcY2/bH2ZWT42qaeRdfxcR96/Jr++SxXTfCINrpujkXuQgDORcABjJ5xFAZkFd92YKyi8J0XPC11jN13evwj/XmWsSSIMLYCjJiAAxgt11HIaBvdKMl/0p/XggzupeBjFt9Cj/vuD+iTayvA6Ag4gNFJ4yjX0V1IgXNbpz/uuzKMlnCwNOpyH3b8GkIP+rGNiCsLiYGxEHAAo5LejN6GNnDOYxvfOjNWIczgjB4IPeY562HU7qILOvw+A4om4ABGIXVt3EbERe5aGJVVdIHGH2HMhAFIo3fz6MZb5iHM5XTa6C6t3OQuBOBYAg6geJaIciL3J1j/iIiVlm1KsNPlcd/h4XQtL7WKrptjm7kOgIMJOIBipQf729C2zXHa6B7k7wMNFwUoXupmm4fAg5f7WC03H3IXAXAIAQdQpLap75eI6trgEKuI+HcINJgIgQcvtI6um8PvS6AIAg6gKLo2ONA6Uqhh5AT+Djwu4lvgMctZD8XQzQEUQcABFEPXBntoo7sG8EdE3LkIAE/bWVr6zxAc8zTdHMDgCTiAwdO1wTPW0Y2d3HnwhuPtjLPchx2zjOUwXLo5gMEScACDpmuDR9zFt10a28y1wCil7o5/hd0d/Ew3BzBIAg5gkHRt8IP70ZN/V8vNXe5iYGrS7+SLMMrC93RzAIMi4AAGR9cGyTa+LQgVasBA7Cwq/Wf6M9OmmwMYDAEHMBjpofk2PDBP2Ta6To3fPSzD8Ak7SNroujluchcCTJuAAxiEtqkvogs3dG1Mz/34yWehBpRL2EF0XXdvXbACchFwAFmlB+L3EXGduxZ6ZacGjNhO2PFbWFA6NW10Iyt+twO9E3AA2aQN/V/CKcIpuQ81FrkLAfqxs6D0t/D7fkoWEfFONwfQJwEHkEXb1B+i69xg/NYR8XtELDzowrTtnJ69DCOJU7CNbmTF+CHQCwEH0KvUtvwlnBkcu21826uxzVsKMERp99K/wr6OKXhnASnQBwEH0BuLRCfh/gKK2WtgLyn4vgwjLGO3CgtIgTMTcAC9aJv6U1gkOlbbiPgcEXe6NYCXSCMsv0XX1SEMH582upBjlbsQYJwEHMBZpeVyX8IW/TFaRNetscpcBzAyrrCM3sdqufmQuwhgfAQcwNkYSRmlbXQLQ2+0GQN9aJt6Ht8WkzIe6+i6Oba5CwHGQ8ABnIWRlNFZRbcw1G4NIAu7OkapjYgrny3AqQg4gJMykjIqbXRLQz96wwYMSeoQ/C1c5BoLIyvASQg4gJMxkjIa2+iWhi6MoQBDlkL192F8ZQxW4coK8EICDuAkjKSMwiqMoQAFSuMr19Ht6pjlrYYXcGUFeBEBB/Ai6aHyS2gTLtkiumBjnbsQgJdqm/oyuq6OWd5KeIF31XJzk7sIoDwCDuBobVO/ioivYSSlRG18Cza2eUsBOL10feV9COBLdRfdAlIjK8DeBBzAUdIbstvcdXCwNrr9Gs68ApPgzGzR1tGFHDoMgb0IOICDtU19Gx4US7ONiI8RcSfYAKbIQtJiOSUL7E3AAewt7dv4Gk7AlmQb3fm9ReY6AAZhJ+i4CCOWJXFKFniWgAPYi30bxdmGYAPgUTuXV34Ln22lsJcDeJKAA3iWfRtF2YZgA2Bvgo7i2MsBPErAATzJvo1ibEOwAXA0QUdR2oh4Wy03q9yFAMMi4AAelB70voTzekO3DcEGwMkIOopy5fMP2CXgAH6S9m3chmWiQ7YNwQbA2aSg41PoYhy6RbXcXOUuAhgGAQfwnbap59F1bnhrNUxtRLwTbAD0w3nZIqyiG1mxfBQmTsAB/M0y0UFrI+JzRNx4gAPoXwo6PkV3XpbhsXwUEHAAHctEB+1jCDYABiF1Or4PO6qGyPJRmDgBB0xcmjG+DW+khmgR3Z6NbeY6APhBCjpuI2KWtxIeYPkoTJSAAyYshRtfwzLRoVlFt2dDmy3AwKXxzk9hd9XQ3FTLzbvcRQD9EnDARKVLKV/DA9mQbKN767TKXAcAB3BadrBcWIGJEXDABLmUMjhtdKMoN7kLAeB4Lq4M0joi3thjBdMg4ICJcSllcG6iCzc8eAGMhEWkg7OObvnoNnchwHkJOGBC2qb+EN0DF/mtohtH2WauA4AzsZ9jUNroOjnst4IRE3DARDgDOxjb6BaI3uUuBIDz29nP4QVDfs7IwsgJOGDknIEdlI/RbXU3jgIwMWk/x20YWxkCZ2RhpAQcMGLOwA7GKoyjABARbVNfRDe2MstcytQJOWCEBBwwUsKNQdiGcRQAfpA+o99HN7pCPs7IwsgIOGCE2qZ+FV24YalZPsZRAHhS+rz+FMZWchJywIgIOGBkhBvZraLr2rClHYC9tE19v4TUZ3cei+g+u72UgMIJOGBEhBtZtRHxsVpubnIXAkB5LAXPbh3dGVkhBxRMwAEj0Tb1ZXQPRvTvLro3P9vchQBQNktIsxJyQOEEHDACwo1s2ui2sFsiCsDJWEKalZADCibggMIJN7K5iy7c8AAEwFm0TT2P7jN+lreSydlGxFv7tKA8Ag4omHAji210wcYqcx0ATEDq5rhfQkp/2ug6OYQcUBABBxSqbeoP4WGnbzfRLRLVtQFAr9Ii8duIeJW7lgkRckBhBBxQoLapbyPiMncdE7INXRsADEDb1J/Cbo4+CTmgIAIOKIxwo3d2bQAwKHZz9E7IAYUQcEBBhBu9ciEFgMFyaaV3Qg4ogIADCiHc6JWuDQCKkLo5vkRElbmUKRBywMAJOKAAwo3etNEtEb3JXQgA7Ct1c9xGxEXuWiZAyAEDJuCAgRNu9GYd3c37be5CAOAY6Xz8p9DNcW5CDhgoAQcMmHCjNx+r5eZD7iIA4KXapp5FN7LinOx5CTlggAQcMFDCjV5sw/lXAEaobeoP0S0h5XyEHDAwAg4YIOFGLywSBWDUnJPthZADBkTAAQMj3OjFO4tEAZgCC0h7IeSAgRBwwIAIN85uHV3XhgcQACalberr6BaQch5CDhgAAQcMhHDj7BbRdW4YSQFgktqmfhXdAtJZ5lLGSsgBmQk4YADSWbfb3HWMVBtdsLHIXQgA5GZk5eyEHJCRgAMyE26c1TYi3nrIAIDvGVk5KyEHZCLggIyEG2flSgoAPCGNrHyNiCp3LSO0jYjXnkOgXwIOyES4cVaupADAHtLIypeImGcuZYzW0XVyCDmgJwIOyKBt6ovoHiY4rTa6kZRV7kIAoCRtU3+KiOvcdYyQkAN6JOCAnmkHPRsPEADwAukFzG14Rjk1zyjQk/+duwCYEuHG2Syq5cacKwC8QLXc3EXEm+i+kHM6r8JCV+iFDg7oiXDjbK6cgAWA03FK9mwW1XJzlbsIGDMBB/QgPSh8jS7B5zScYAOAM2qb+kNEvM9cxtgIOeCMBBxwZsKNszDLCgA9SFffPoUO1FPSfQpnYgcHnN+XEG6c0iKEGwDQi/RF/E1EbPNWMiq3KTgCTkwHB5xR29S3EXGZu44R+VgtNx9yFwEAU6Mj9SzeOG0PpyXggDMRbpxUGxHvtHMCQF6eb07KPjE4MQEHnEFqO7zNXcdI+PAHgAGxfPSk2oh4XS0329yFwBgIOODEhBsnZZkoAAyQ5aMn5XkHTsSSUTihtqlfRfdhz8vdhQ97ABikneWjPqdf7lV0+02AF9LBASeSwo2v4U3GKbgRDwAFaJt6Fi7GnYrnH3ghAQecgM3iJ+U2PAAUxHPQSd1Uy8273EVAqYyowGn4UD8N4QYAFKZabtpquXkdEYvctYzAddpvAhxBBwe8kHNpJ+FSCgCMgOeik3lTLTer3EVAaXRwwAukM2mXeasonnADAEYi7ZCwR+LlvqT9bsABdHDAkZyDPQln0QBghDwnnYTnJDiQDg44gnOwJ+FDGwBGyhnZk3A+Fg4k4IAD7WwKdw72eIsQbgDAqKUdEkKOl3mV9poAezCiAgdwBu0k3HgHgAlJna9fImKWuZSSuTQHe9DBAYf5FMKNlxBuAMDEpEXir6MbT+U4t21Tz3MXAUMn4IA9tU19HS6mvMRH4QYATFMaS30TQo6XcFkFnmFEBfbQNvVFdK2VHEdbJQBg3PflLGmHJ+jggGekpNxyp+MJNwCAiPiuk2OVuZRSeS6FJ+jggCd4y/Biwg0A4EHpOshl7joK9bFabj7kLgKGRgcHPO02hBvHEm4AAI9Ku7kWueso1Ps0Qg3sEHDAI9qm/hARPjiOI9wAAJ4l5HiRW0tH4XtGVOABlooerY1u8ZUN6QDA3oyrHM3SUdgh4IAftE09i4g/I6LKXEpphBsAFCt9/s9+/OvVcrPqu5apEnIc7a5abt7mLgKGQMABOywVPZpwA4BipLb+i4j4R3Sf+bM9/m2riNhGxB8RsaqWm+15qps2IcfR3lXLzU3uIiA3AQfs8KF6FOEGAIOXQo3fogs2TtGluY0u9BB4nJjnsaO90XHE1Ak4IGmb+jLcFT+UcAOAQUuf77/F+bsz15ECj2q5uTvzf9foCTmO0kbEL/ZxMGUCDoi/3+p8DXs3DiHcAGCw2qaeR/fiYpbjvz66sOPf0e1H8IXzCEKOo6yq5eZN7iIgFwEHk5f2bvwZeR6ASvbWGyoAhiZ9rt/GsE69ryPi9+jCjm3mWooi5DjKx2q5+ZC7CMhBwMHktU39JYb1EFSCq2q5WeQuAgB2pa6NLzHsjsz7UZbfdUHuR8hxFC+imCQBB5PWNvV1RHzKXUdhhBsADE7b1B8i4n3mMg61jYi7EHY8S8hxsDYiXusYYmoEHExW2rvxZ+46CiPcAGBwRvLldxvdGMvCl9KHtU39NSLmuesoyLpabl7nLgL6JOBgkuzdOIpwA4DBGUm48aP7nR0LC0q/Sc9vX+P8F3HG5KZabt7lLgL6IuBgkuzdOJhwA4DBGWm48aO7iPi3z+GOkOMob6rlZpW7COiDgIPJsXfjYItqubnKXQQA7Jrg53kbXdjxeer7OoQcB2sj4hfdQEyBgINJSXs3vsawt6sPiXADgMGxRyu2EfE5JjzCYtz4YKtquXmTuwg4NwEHkyHtP5hwA4DB8cX2J/dXWCZ3EtSLq4O9q5abm9xFwDkJOJiMtqk/RcR17joKYes2AIPk8/xR25jgFRYhx8FeT33EiXETcDAJbVNfRMSX3HUUYh3dMqpJtrwCMFxtU88i4q/cdRTgflfHKnchfWibeh5dyMHzttGFHJ7zGKX/nbsAOLeU7N/mrqMQwg0Ahszn+X4uIuJr29R/tU19ncZ6RisFOcZq9zOLaS3nZWJ0cDBq9m4cpI0u3NC2CMDgeEv/IvcXWD6OeXylberLEILt6+0U97Ywfjo4GLvbEG7sQ7gBwNC9z11AwaqIuIyIv9qm/prCotGplptFRCwyl1GK27F39jBNOjgYLXs3DiLFB2CwdG+cxTYiPkbE3dhGU9umvo0u0OFpTscyOgIORiktIfszbNTex1V64wEAg9Q29deImOeuY6TaiPgcETdjCjrapv4zdPHuw+lYRsWICmN1G8KNfdwINwAYstS9Mc9cxphV0Y3//L9tU9+ml0Rj8Ca65ek87X1ayA+joIOD0Wmb+jpsh97HolpubBwHYNB0b2SxiIjfSz8zq6N3b67oMRoCDkbFB9ne1tVy8zp3EQDwFFcxsltFd3lllbmOo6XuhK/h2fA5d9Vy8zZ3EfBSRlQYmy/hA+w52+jaNgFg6FxOyWseEV9LvrySLsS9y11HAS5SFzQUTQcHo9E29YfwIPQc52ABKILujUHaRtfRschcx8E8J+6ljYjX1XKzzV0IHEvAwSik9sM/c9dRgDclt5kCMB1tU/8VEbPcdfCgbRQYdDgfuxenYymaERXGwhue510JNwAoQeremGUug8fNIuK2beq/0v9WRUjL1XWxPm1uVIWS6eCgeFoO9+JiCgBFaJu6iq4rc5a5FPa3jUI6Ovz9tRejKhRLBwdFS6Mpwo2nrYQbABTkOnz5LM0suo6OwS8jTadQ30b3JZ6HVaE7mkIJOCjdp9wFDNw2ug9xABi89Hb9t9x1cLR5FHB1xWWVvRhVoUgCDoqVfunOc9cxYG1EvE1vKgCgBNfh3PsYzONb0DHLXMuD0jjNx9x1DNz7of7vB4+xg4MipV+2f4aHoKe8rZabu9xFAMA+UvfGX+GzfYwW0e3o2Gau4ydtU3+JiIvcdQyYqyoURQcHpboND0BP+SjcAKAwn8Jn+1hdRsRfbVN/SEHWkLis8rR5SZdyQAcHxUm/ZC0+etxdtdzYuwFAMVJn5l+566AXbUR8rpabD7kLuZeW1n8NAdtj2oj4xdgzJdDBQVFS6m+x6OO20b2JAICSuIg2HVV0ux3+apt6EKMhaemo56fHuapCMQQclMZoyuMsFQWgOOnt+WXuOujdLCK+pEWkr3IXk0Z7b3LXMWAXQ76MA/eMqFCM9Ev1a+46BuwqbQQHgGK0Tf01XEWjW0T6LveLGn8/PmkbEa9z/28ET9HBQRHSaIrWuMcthBsAlCa9vJhnLoNhuIy0iDRvGfE2uq5YfjaL7pQzDJaAg1JcR/dLlZ+tq+XG3CgAJbJXi127+znmWQrouhOcRX3c+yGMFMFjBBwMXtqsbvnYw9ro3jQAQFHSVTRflHjILCK+tk39JT0H9iotHX3X939vQQSTDJaAgxIYTXncVbXcbHMXAQBH8PKC51xExJ85xlaq5eYmIu76/u8txDwFlDA4Ag4GLf3ynGcuY6hu0sZvAChK+sI6y1sFhcg5tnIV3WJNfvYp7ciDQXFFhcFKvzT/CmdhH7KulpvXuYsAgEP5fOeF7qLrYO1lEWjaN/FnH/9dBbqplhujPAyKDg6G7H14+HmIvRsAlMznOy9xEd21lV6uedjH8aRrC0cZGh0cDJK0/ElvjaYAUKK0MPKv3HUwGquIeJdCiLNqm/pLdOEK31tVy42rMwyGDg6Gynbmh9m7AUDJfL5zSvPobwmpfRwPs3CUQdHBweCkX5Iup/zM3g0AipUWRH7NXQejtY1uN8fqXP8F/h5+VBsRv/S1FwWeooODQUmLx5yN+1kb3ZsDACiVz3fOaRYRX9umPtt1jxSefDzHf3bhPL8zGAIOhuY6nI17yMc+5ksB4BycfadH19GNrczP8R9eLTcfIsIz2c+u044dyErAwWCkX4rS35/dVcvNTe4iAOAFfL7Tp1mct5vjbXTdtXzPiDnZCTgYEovHfmY0BYCipQWQs7xVMFFn6eaolpttOB37kHnb1C7NkJUlowyCpU2PenPOZVkAcE7p7flf0c3oQ0430Y38nqzzwunYB22r5eaX3EUwXTo4GArdGz+7EW4AULhPIdxgGO67OV6d8D/T6difzXo62wsPEnCQXVo8dsoPmzFYhy3dABQsfZG8zF0H7JhFF3J8OMV/WOoGMUr8s9/OdckGniPgIKv0y0/3xs+u3BIHoHA+3xmq921T/3mKqx+p29Yy+O95vicbAQe5XYfW1R85CQtA0dKiwXnuOuAJr6Lr5rg8wX/WxzCq8qNLZ2PJwZJRskm/9P4MAceudbXcvM5dBAC8RNvUf4XLKZTjLl7YPWth/oNW1XLzJncRTIsODnJ6H8KNH5njBKBozsJSoIt44TnZNKpif9r35qc+0QvPEXCQhcVjDzKaAkDRUnfmb7nrgCPMIuLrSxaQVsvNh+gWxfPN+9wFMC0CDnKxeOh76/ShCAAl051J6d63Tf31BVdAdON+b36iPSewFwEHvUutavPMZQyND0MAipY+3y8zlwGnMI+Iv44Zr0jduEZVvqeLg94IOMhB98b3jKYAMAY+3xmTKo4cWUldudvTllO0WdvU17mLYBpcUaFXqUXtNncdA+JqCgDF8/nOyK0i4u0hV1ZcVflJGxG/vORSDexDBwd906L2PaMpABQt7SrQvcGYzePAkZV0VeXmTPWUqIoIXRycnYCD3qS3O7PMZQzJjdEUAEbAYtFv7iLibUQsMtfB6d2PrBzyJf1jGFXZ9dsLlrfCXoyo0Iv0y+zPEHDc20bEa216AJQsnX3/M3cdA/JLtdxsI/5+9rmI7mzuq5xFcXJ3EXG1z3OcUZWffHQ5kHPSwUFfrkO4sWuvD0UAGDijKd8s7sONiIhquWmr5WaRdm39Et24gs/+cbiIrpvj2eAqjarcnb2icrxvm3qWuwjGS8DB2aU3GL/lrmNA7tKHHQAUK42ezjOXMRRtRLx77P9YLTfbarl5Vy03/090+7dWfRXG2byKLuS42ONfexXCrV128nE2Ag76cB1mc++1YbEoAIWzWPQnn/ftzExdHW9CV8cYVBHx5blTsunvjY99FFSIS10cnIuAg7PSvfGTd0ZTABgBi0W/aeOIaxn3XR3RBR1XEWHxeLnet0395akFmtVycxM6d3bp4uAsLBnlrFKi7RdYZ5Xe2ABAsSwW/clVtdwsTvEflBZS/isiLk/xn0fv1hHxdncXyy4/Oz/55bH/X8GxdHBwNro3fvLobC4AFMRoyjfbU4UbEd1Cymq5uQrjK6V6FRF/PrZ8tFpu1mFUZZeXoJycgINzsnvjm5v0oQYAxbJY9Cdn2av1wPjK9hz/PZxFFV3IcfnI//0m/O95zy4OTs6ICmeRujf+CgFHRPch9truDQBK5rP9J72OnqYvzP8KAVNJblJQ9Z10eeVLhnqGaJG6luAkdHBwLro3vrFYFIAxsH5lTroAACAASURBVFj0e72Onu5cX3kTllWW4rpt6tsfl49Wy81d+N/wni4OTkrAwcnZvfGdVfoQA4BipeWX17nrGJBFrtHTtKfjTUS8johFjho4yGVEfH3gwoquhW/s4uBkBBycg+6NbywWBWAMLBb9po0BLIqslpv1zkLSReZyeNpPy0fT9ZDsfx8NhC4OTkbAwUnp3viOxaIAFK9t6uvovqDR+Tyk05ZpIamgY/hm0XVy7P4suZTzjS4OTkLAwanp3ugM4u0OALxEeqvqi8c32+i+lA6OoKMI311YSTvadPt2dHFwEgIOTkb3xncsFgVgDD6FFxe7Pg79813QUYTbnZBjERaO3hOm8mICDk7pIjwERUSs04cVABQrLRa9yF3HgKxK+nwXdAzebdvU97ttdP12Lh5YxgoHEXBwSlLXjlZDAIqWvmTc5q5jYIr8EroTdDgvOzzXbVPfVsvNKoRQEd2LUteaeJH/lbsAxiG12XkQirirlpu3uYsAgJdIb5Z90fhmkUKC4qXOnPcRMc9bCTsWEfE5Ir6Gbug2In4Z+igYw6WDg1PRvdHRvQFA0dKVB+HGN6NaHF4tN6tquXkTEVfRLU0lv8voXhQu8pYxCLo4eBEBBy/WNvVFdKevpu5mSGfjAOBIOjK/N6izsKdSLTeLarn5JbqXM96W5/cq7Ly596/cBVAuAQen4HLKyN7uADBNbVNfR/dFi842BnoW9lSq5eYmukWknmPym+UuYCBm91dm4FACDl4kzXHOM5cxBJ/NCgJQsrapZ2Hk9EeDPwt7CtVy01bLzYfogo67zOVAhN9FHEnAwUvp3ojYpocCACjZbVhwuKuos7CnkC6uvI3u4so6dz1M2iy9SIWDCDg4WnrTY1ZQSycAhUv7tOa56xiYyS4OT4tIX4f9HOSli4ODCTh4Cb90ItZTe7sDwLi0TV2FxaI/WlTLzeQ7GHb2cywyl8I0zdNVJ9ibgIOjpIch3RsTfrsDwGgYTfleGz7f/5b2c1yFsRXyMA7PQQQcHOs6PAytquVmlbsIADhWmnH3wuJ7k1gseihjK2RymcbiYS8CDo7lPrXdGwAUzGjKg9ZpLINH7IytuLZCXy5zF0A5BBwcLN2lnmUuIzfdGwCU7n34PP+R0ZQ9pLGV+2sr28zlMH6/pUAWniXg4Bhm4SKuchcAAMdKoynXuesYmIWXF4dJ//96HRG6Xjgnu//Ym4CDg6QHoqlvM15Uy802dxEAcAyjKQ9qw+jpUVI3x7vogg5LSDkX1xvZi4CDQ9m94QEIgLIZTfnZZy8vXqZabtZpCannJM5hll60wpMEHOwtbTC+zFxGbro3ACiW0ZQHbavl5kPuIsYi/f9SNwfnYEyeZwk4OMRl7gIGwFsJAEpmNOVn9mqdmG4OzuTCyVieI+DgEFMfT9G9AUCx2qb+EEZTfnRnsej56ObgDC5zF8CwCTjYi9OwEeEtBACFapv6VVjS96M2nIU9O90cnJgxFZ4k4GBfujd0bwBQLqMpP/vos70/qZvjTURs81ZC4ar04hUeJODgWWnWbZ65jNy8dQCgSG1Tfwon3n+0rpabm9xFTE0aB3odEYu8lVC4qb945QkCDvYx9VYw3RsAFMnVlEcZTcmkWm7aarm5ioi30Y0JwaHmlo3yGAEHT2qbugrLfHRvAFCc9BluNOVnC4tF86uWm7voujlWmUuhTFN/AcsjBBw85yIiqtxFZHSnewOAQr0PC8J/ZLHogFTLzbZabt6El0kc7jKFuPAdAQfPmXo6+jl3AQBwKKMpj3pXLTfGIgZmZwGp/23YVxXdi1j4joCDR6XZtikvJVtpYQWgNEZTHrWqlptF7iJ4WHrm+iWMrLA/y0b5iYCDp0y9e0O7JAAl+hRGUx5iNGXg0gJSIyvsy7JRfiLg4CmXuQvIaK17A4DStE19EdP+/H7Mx2q5Wecugv0YWeEAU38hyw8EHDyoberLmPZyUbs3ACiK0ZRHbSPiJncRHMbICnu6zF0AwyLg4DH/zF1ARlszugAU6Dam/XLiMVcWi5ZpZ2RFQMVjqtS5BhEh4OABaZZtyr8ozH0CUJS2qa9j2p/dj7kzclq+arl5FxFXYWSFh1k2yt8EHDzkMncBGbURcZe7CADYV3ox8T53HQPURvelmBFI3bVvohs5gl0Xlo1yT8DBQ6acgn7WxgpAYYymPOydz/RxSYtiX4e9HPxMBxsRIeDgB21Tz2Pap+UWuQsAgH21Tf0hIuZ5qxiklX1a42QvB49wTYWIEHDwsyl3byyq5WabuwgA2Efb1K/CaMpDjKZMwM5eDoiImKXfiUycgIMfTbm9y2lYAIqQTsJ+yV3HQH32wmIaUpfO67B8lM6UX9SSCDj4W9vUlzHdGd5VmusEgBJ8immPlD5mXS03H3IXQX929nJ4juMydwHkJ+Bg1z9zF5DR77kLAIB9tE19ER7kH2NkYYJSx86bsHx06qr0+5EJE3AQEX+3uk71F8LWIjIASpBOId7mrmOgbnRjTtfO8tFF7lrIasovbAkBB99c5i4gI90bAJTCSdiHbSPiY+4iyK9abq4i4l3uOsjmMr24ZaIEHNyb8lIeZ8YAGDwnYZ90VS03Fk0SERHVcnMTxpWmbKpd6YSAg/i73XWqZ5UWHogAGDonYZ90Uy03q9xFMCwurEyaMZUJE3AQMe2U03gKAIPmJOyTtmE0hUeknSxvQsgxNRfGVKZLwEHEdMdTtt74AFCA23AS9jHvdGLyFGdkJ2vKL3AnTcAxcRMfT/HGB4BBa5v6MjyoP+auWm7uchfB8O2ckRVyTMdvuQsgDwEHU/3hbyPCQxEAg5X2bnzKXcdAtWGJJAdInT5Cjul4lV7kMjECDqb6VuhOSysAQ5Xmx52EfZyrKRxsJ+RYZC6Ffkz1e86kCTgmLL0ZmuWuI5PPuQsAgCe8j+mOkD7HaApHq5abtlpurkLIMQVT3TM4aQKOaZvqD/02LZwCgMFpm/oiIq5z1zFQRlM4CSHHJBhTmSABx7RNtW1L9wYAg5Qexm9z1zFgRlM4GSHHJEz1+85kCTgmKj1AzTKXkcsidwEA8IgvYe/GY4ymcHJCjtGbasf6ZAk4pmuqaebCmx8Ahqht6k9h78ZjjKZwNkKOUXuVljYzEQKO6Zpqmvnv3AUAwI/s3XiW0RTOSsgxalN9sTtJAo4JSuMpU3xDtNXaCsDQpKtm9m48zmgKvRByjNY/cxdAfwQc0zTVFNPDEQCDklqnb8Pejcdsw2gKPRJyjNKFMZXpEHBM01RTTNdTABgaezeeZjSF3gk5RmmqL3gnR8AxMSm9nOeuI4N1tdxscxcBAPfapr6MiMvMZQzZTbXcrHIXwTQJOUZnqi94J0fAMT1TTS9/z10AANxLezc+5a5jwLYR8TF3EUybkGNU5rkLoB8CjumZanq5yF0AAETYu7EnoykMgpBjNKp0rYqRE3BMzzx3ARnceUgCYEBuw96Np3w0msKQpJBjlbsOXuzX3AVwfgKOCUmp5RTfFv07dwEAEBHRNvV1THdcdB/rarn5kLsIeMDbiFjnLoIX8bt3AgQc0zLV1NJ5WACya5t6HvZuPMdJWAYpdQO/CSFHyWZp/xEjJuCYlimmlgvjKQDklvZufMldx8C9q5YbXx4ZrJ2Qw7Nluea5C+C8BBwTkdLKWe46MjCeAsAQfI1pjonua1UtNze5i4DnCDmKN9WDC5Mh4JiOee4CMmir5cZ4CgBZtU1tqejT2jCaQkFSp5GQo0zz1FHHSAk4pmOKaaVwA4Cs2qa+jIjLzGUM3VW13GxzFwGHSCHHu9x1cJQpju1PhoBjAlJKOc9dRwbGUwDIJo2H3uauY+AWui0pVbXcLEL3UYmmenhhEgQc0zDPXUAGxlMAyCa9XPiau46B24Y34BQuhRyLzGVwGB0cIybgmIYpppTCDQByslT0eVcunTEG1XJzFZ49S1I5FzteAo5pmGJKaTwFgCwsFd3Lx2q5WeUuAk7oKiKcOS7HPHcBnIeAY+Tapp7F9M7DGk8BIAtLRfeyrpabD7mLgFNyPrY4UzzAMAkCjvGb5y4gg1XuAgCYHktF99JGxNvcRcA57IQcDN88dwGch4Bj/KaYThpPAaBXloru7Z2TsIxZOh/rskoB2qae4hj/6Ak4xm+eu4AMjKcA0JudcMNS0act0sUJGLX09/lN7jp41hQPMYyegGPEUqvs1B627mxkB6Bnn8JS0edsw0lYJqRabt6Fsemhm+cugNMTcIzbPHcBGfyRuwAApqNt6uuwVHQfb72AYILeRhfuMUyvUgceIyLgGLcp7t8wngJAL9L89qfcdRTgXdpLAJOSQr234bLKkM1zF8BpCTjGbZ67gJ6tLS4DoA8upuxtVS03dhEwWSncM541XPZwjIyAY6Tapp7nriED11MAOLvU0vwlprfn6lBOwkJYOjpwLqmMjIBjvOa5C8jAeAoAffgaEbPcRRTA3g1I0tJRo1rDM2ubepa7CE5HwDFeU2u3as33AnBubVPfhosp+/hYLTer3EXAwNjHMUzz3AVwOgKO8ZrnLqBnujcAOCsXU/a2qpabD7mLgKFJu+KuctfBT6b2YnjUBBwjNNH9G87DAnA2bVNfhosp+7B3A55QLTd3YR/H0OjKGxEBxzjNcxeQgQ4OAM4iXUwRbuzH3g14hn0cg/MqLY9mBAQc4/SP3AX0bO1hCoBzSMvnvoaLKfuwdwP2Zx/HsMxzF8BpCDjGaZ67gJ45DwvAyTkHexB7N+AA9nEMjj0cIyHgGJnURju1B7FV7gIAGKUvYTZ7H/ZuwBHSPo5F7jqIiOm9IB4tAcf4zHMX0LNWOywAp5bOwc5z11EIezfgeO8iYpu7CITZYyHgGJ+p7d9Y5S4AgHFxDvYg9m7AC6Rw0KjKAEz0EuXoCDjGZ567gJ45DwvAyTgHexB7N+AEUkj4MXcdTO571CgJOEYkLUOb5a6jZ6vcBQAwDmmP1W3uOgph7wacUAoLnY7Na2qd8KMk4BiXee4CeratlhsfBAC8WAo3vuauoyBv7N2AkzOqktc8dwG8nIBjXKa2HGeVuwAAyucc7MHeecEAp5d+royq5FO1TT3LXQQvI+AYl6ndb7Z/A4AXSeHG15jeiOexFtVyc5O7CBgroyrZzXMXwMsIOMZFBwcAHOZLTO/z81jr6E5aAudlVCUfezgKJ+AYiTQ7PKXW2m213GxzFwFAudqmvg1v6/bVRsSVvRtwfkZVshJ4F07AMR5T+2Fc5S4AgHKlcOMydx0FubJ3A/pjVCWbee4CeBkBx3hMrZ3K/g0AjtI29WUINw5xUy03d7mLgAkyqpJB6oynUAKO8ZjaD6JEG4CDpXDjNncdBVlVy429G5BB6pqy1Ld/89wFcDwBx3jMcxfQo1abLACHapt6HsKNQ2wj4m3uImDiPkb3s0h/ptYZPyoCjhGYYBvVKncBAJQlfVZ+yV1HQdqIeGupKOSVfgaNqvRrat+tRkXAMQ5T+yH8T+4CAChHCje+xrSujb3UO92SMAzVcrOKCHtw+jO171ajIuAYh6m1Ua1yFwBAGdqmrkK4cahFtdwschcBfOdddJ1V9CCNNFIgAcc4TCplTCk2ADxJuHGUdbXcaIeHgamWm21EfM5dx4RM6vvVmAg4xmGeu4AerXIXAMDw7YQbHlL3t42IN7mLAB5WLTcfwsLRvvx37gI4joCjcBNcMGoeGIAnCTeOYqkolEGHVT98fhRKwFG+We4CembBKADPuQ0Pp4eyVBQKYOFob+a5C+A4Ao7yTe0BbpW7AACGq23q24i4yF1HYW4sFYWiWDjagwl2yo+CgKN8v+YuoEdtWrAEAD9J4cZl7joKc1ctN+9yFwHsz8LR3sxyF8DhBBzlm+UuoEdaZwF4kHDjKOswzw+lugkLR89NB0eBBBzlm+UuoEd/5C4AgOERbhyljYgrS0WhTOln92PuOkZuSp3yoyHgKFjb1PPcNfRMBwcA32mb+jKEG8d4a6kolC3tzvFzfD6z3AVwOAFH2Wa5C+iZX+AA/C2FG7e56yjQVbrEAJTPDp3zmeUugMMJOMo2y11AjywYBeBvwo2jLVxMgfFIYeUqcxmjNcGO+eIJOMo2pbkw3RsARIRw4wVW1XJjqSiMjy6O85nlLoDDCDjKNstdQI8sGAVAuHG8dUS8zV0EcHppn84idx0jNctdAIcRcJRtlruAHm1zFwBAXsKNo7mYAuPnosp5TKljfhQEHIWa4DyYERWACRNuvIiLKTByaVfdInMZYzTLXQCHEXCUq8pdQJ88mAFMl3DjRVxMgen4GF3HFqczy10AhxFwlOtV7gJ6JNwAmCjhxou4mAITkro4PueuY2zapp7S967iCTjK9Y/cBfRIwAEwQcKNF7lzMQUm6SZ0cZzapDrnSyfgKNeUftD+J3cBAPRLuPEi64gQbsAEpWXCujhOa567APYn4CjXPHcBPVrlLgCA/gg3XqSNiDcupsCk6eI4rf/KXQD7E3AUqG3qKXVvRDgRCzAZwo0XEW4AujhOzw6Oggg4yjSpH7K0MAmAkRNuvJhzsMA9XRynM8tdAPsTcJRplruAHq1yFwDA+Qk3Xsw5WOBvqYvjLncdIzHLXQD7E3CUaZa7gB5JngFGTrjxYjfOwQIP+Ji7gLFwKrYcAo4yTelE7H9yFwDA+Qg3XmxRLTfvchcBDE8a815kLmMsprYDsVgCjjJN6QfMLDHASAk3XmxdLTfOwQJP0cVxGjo4CiHgKNM8dwE9MqICMEJtU1+HcOMl1hHxJncRwLDp4jiZKb1gLpqAg0GzMA1gfNqmvo2IT7nrKJhzsMAhfs9dwAj8mrsA9iPgKEzb1PPcNfRom7sAAE4rhRuXuesomHADOEh6YbjKXAb0QsDBkG1zFwDA6Qg3TuJNtdzYTwUcShfHy8xzF8B+BBzlmecuoEce4ABGQrhxElfCDeAY6ZT0NnMZcHYCDobs/+YuAICXaZu6apv6awg3XuoqfUEBONbn3AWUrG1ql1QKIOAozz9yF9Ajb6kACtY2dRURX2Na3YfncCPcAE5gES4UvoRLKgUQcJRnSj9YfgEDFGon3PDG62UW1XLzLncRQPnScuK73HUUzOdZAQQc5ZnlLqAvTsQClCm18Qo3Xu6uWm6uchcBjMrH3AUUbEovmosl4CjPLHcBAPAY4cbJrCNCuAGcVLXcbMPJ2GP9d+4CeJ6AoyCp3XcqVrkLAOAwO+HGlD6vzmEd3TlYo5rAOVg2epxZ7gJ4noCjLN6GATBIbVNfhnDjFLYh3ADOqFpu7sLJ2GP4fCuAgIOh+iN3AQDsJ4Ubt+Hh76XaiHgr3AB68HvuAgrkZXMBBBxl8UMFwKC0Tf0hunCDl2mj69xwIh3owyJ3AXAOAo6yTOnNmAc8gIFrm/o2It7nrmMEhBtAr9KyUSdjD5R2TTFg/yd3ARzkv3IX0CPtuQADlZZe30bERe5aRuJKuAFk8Hv4PX6oKb1wLpIOjrJMKTHc5i4AgJ+lcONreCg+lau08A+gV5aNMkYCDgYptc0BMCA7Z2CnFLif01W13CxyFwFMmoD1MPPcBfA0AUdZZrkLAGCahBsnJ9wAhuBz7gLglAQcZZnlLqAnq9wFAPBNOgP7Ncwen8o74QYwBKlr2g6g/U1pJ2KRBBwAwKNSuHEbwo1TWVTLzU3uIgB26OLYny7GgRNwFKJt6lnuGnq0zV0AAH+fgb3NXceILKrl5ip3EQA/sIeD0RBwlGOWu4Ae/U/uAgCmrG3qqm3qLxFxmbuWERFuAINULTdtCDn2pZtx4AQcAMDfnIE9C+EGMHS/5y6gEEZUBk7AUY4ppYUWHQFkkC6l/BUe4E5JuAEMXrXc3EVEm7sOeCkBRzmm9LDplytAz1xKOQvhBlASYyoUT8ABABPXNvWHcCnl1IQbQGn+nbuAEkzs+ENxBBwMkREVgB6kZaK3EfE+dy0js46Id7mLADiEMZW9zXIXwOMEHOX479wF9CVtcgbgjHaWiV5mLmVs1hHxxmcZUChjKhRNwFGOWe4CABgHy0TPRrgBlM6YCkUTcDA029wFAIyZZaJnI9wAimdMZS+z3AXwOAEHQ7PNXQDAWLVN/SksEz0H4QYwJsZUnjbLXQCP+z+5C2BvHkYBOErat3EbERe5axkh4QYwNv8O+5kolA6OcpiTBuBgad/G1xBunINwAxgdYyqUTMDB0GxzFwAwFm1TX0QXbgjJT0+4AYyZMRWKJOBgaP4ndwEAY9A29YeI+BJGHM9BuAGM3R+5CxiwX3MXwOPs4ACAEbFv4+xWEfFWuAGM3F10nyVQFB0cADAS9m2c3aJabnRuAKOXfs8ZU6E4Ao4CtE09z10DAMPWNvVl2LdxTotqubnKXQRAj4ypUBwBB0OzzV0AQGnapv4UXSuxfRvnIdwApkgHB8Wxg4Oh2eYuAKAUad+Gro3zEm4Ak1QtN9u2qbcRMctcCuxNBwcAFCiNL/4Vwo1zEm4AU6eL42fz3AXwOAEHABQmnYD9GkZSzkm4AWAPB4UxogIAhUgjKV/C26Nzu6mWm3e5iwDIrVpu7tqmzl0G7E0HBwAUIJ2A/TOEG+d2JdwA+M4qdwGwLwFHGcxXA0xY29TX0YUbs8yljN1VtdwschcBMDD/zl0A7MuIShmmNGO9zl0AwFCkkZTbiLjIXcsECDcAHrbKXQDsSwcHg1ItN23uGgCGYOdKinDjvNoQbgA8qlpu1tH9roTBE3AAwMC4ktKbNiLeCDcAnrXKXQDsw4gKAAyEKym9ug83jEYCPO+P0FFIAXRwAMAAtE19Ed1IyjxzKVMg3AA4zCp3AUPSNvUsdw08TMABABm1TV21Tf0pus4NIynnt46IX4QbAPuzh+Mns9wF8DABBwBk0jb1q+h2bVznrmUi1tF1bnhIBzjcKncB8BwBBwBk0Db1dUT8GRGvctcyEasQbgC8xB+5C4DnWDIKAD2ySDSLRbXcXOUuAqBwq9wFwHN0cABATywSzUK4AXAC9nBQAh0cAHBmqWvjfdi10berarlZ5C4CYETWIaRnwHRwAMAZtU09j27XhnCjX8INgNOzh4NBE3AAwJm0Tf0huisps6yFTEsb3TLRRe5CAEZolbsAeIoRFQA4sXT+9TZcSOnbfbixzl0IwEj5/cqg6eAAgBNKXRvOv/ZvHRGvhRsA55NObfs9y2Dp4ACAE9C1kdU6us4N2/0Bzm8dPusYKB0cAPBCujayWlTLzWvhBkBvLBplsHRwAMCRdG1kd1MtN+9yFwEwMUZUGCwdHABwBF0b2V0JNwD6Z9cRQ6aDAwAOoGsjuzYi3lbLzSp3IQATtoqIeeYa4Cc6OABgT7o2srs/A7vKXQjAxOniYJB0cADAM9qmnkfEpxBs5ORSCsBw/Cd3AfAQAQcAPKJt6ioi3kfEde5aJm4REe+EGwCDsc1dQGbz6MZ0GBgBBwA8IHVt3EbELG8lk+dSCsDAVMvNqm3q3GXATwQcALAjdW3cRsRF7lqIq2q5WeQuAoAHrcPoJgNjySgAJG1TX0fEXyHcyK2NiNfCDYBBs2iUwdHBAcDkpdOvn8LJuyFYR3cGdpu7EACe9D+5C4AfCTgAmKw0jnId3SJR8ruLbizFMlGA4VuFz08GRsABwCS1TX0RXdfGLHMpdCwTBSjLNncB8CMBBwCT0jb1LLpgw56N4bBMFKAw1XKzdUmFobFkFIDJaJv6Q0T8GcKNobBMFKBsq9wFwC4dHACMXtvU8+hOv87yVsKOdUS8sW8DoGjb3AXALgEHAKNlHGWwFtVyc5W7CABezCUVBkXAAcDouI4yaPZtAIzHKnzWMiACDgBGpW3qy+getmZ5K+EHbUS8rZabVe5CADgZY4YMioADgFFom/pVdOMo88yl8LN1dOHGNnchAJxOtdysXVJhSAQcABQtjaN8iojLzKXwMPs2AMZtHRGvchcBEQIOAAqWzr7+FhFV3kp4hH0bAOM3xTGVf+QugIcJOAAojj0bg9dGdwJ2nbsQAM7uj5jeeKgXKwMl4ACgGG1Tz6MLNuZ5K+EJq+j2bUzxjR4AkJGAA4DBa5t6Ft2ejYvMpfC0m2q5eZe7CAB6tQqnYhkIAQcAg5UWiL6PiOvctfCkNrp9G3e5CwGgdzr2GAwBBwCDk4KN67BAtAROwAJMmFOxDMn/zl0AAOxKC0T/jK5zQ7gxbIvololuM9cBQF66OBgEHRwADILLKEVpI+KdE7AAJOuwAJwBEHAAkJXLKMVZR7dvwwlYAO7p4GAQBBwAZCHYKNIius4ND7IA7PpPuHTGAAg4AOiVYKNIRlIAgMETcADQi7apZ9EFG5d5K+FARlIAeM4qus94yErAAcBZCTaKtggjKQBAIQQcAJyFYKNobXRdG3e5CwGgCLr8GAQBBwAnJdgo3joi3lbLzTZ3IQCUoVpu2rapc5cBAg4ATkOwMQofq+XmQ+4iAChSGxFV7iJ6MstdAA8TcADwIm1Tv4qI30KwUbI2uq6NVe5CACjWOqZzIW2WuwAeJuAA4CjOvY7GXXT7NiwSBQCKJuAA4CCCjdFooxtJucldCACjMKUODgZKwAHAXtqmvoyIf4WHlzFYR9e1Yes9AKfyf3MXAAIOAJ6Ugo33Yd50LG6q5eZd7iIAAE5NwAHAT9qmriLiOrqOjVneajiRbXRdG6vMdQAwTroCyU7AAcDf0qnX+4soUzn1NgUWiQJwbj5jyE7AAYBTr+PVRhds3OUuBADg3AQcA5Xeos6iW+b3a85agPFqm/oiumBjnrkUTm8VEW91bQDQEyMqZPe/chfA37Pur+JbmPEqJtoaXi03/p6EM0u/cy6jCzZmWYvhHJx/BSCLtqn/v9w19Gyd/vhPRKxcJ8tPB0cmbVPPI+Kf0YUar7IWA0yC/RqTsIpuJGWbuQ4AmIJXsfNdrm3qNrrP4j9C4JGFt+U9SW9ML6ILNS4ylzNYOjjgE8TNSQAAIABJREFU9FKg+lv43TNmujYAyK5t6v83vETZtY0u8Pi3fVj98GXyjIQahxNwwGns/P55H8ZQxm4VujYAGIC2qb+GvV6PaaO7aibsOCMjKmeQlvb9M1wjAHqWxlDeRxdueIMybro2AKAc9zvQLtum3kYXdnz2guK0BBwnkt6WXkfEv8LbUqBnbVNfRvf7Z563EnqyCl0bAFCqWXTfHa/bpl5FxO/VcrPIWdBYCDheaOdt6WXeSoCpsTR0knRtAMC4zCNi3jb1+4j4PSJunHg/noDjSGlp3/vwthTomW6NyVqFrg0Ahu2P8HxyrFl03y9/a5v6cwg6jmKh44EEG+dlySg8TLfGpOnaAGDw0gsYy81P6ya6ZwBBx558mdxT+nLxKVxDObdfvJ2Eb3RrTN5dRLzzexGAofIC+OzaiNDRsScBxzPS8tBPYcdGnySVTFrb1K+i69ZwCWW62ujGUZyRA2CQ0gvg2xBs9EVH5x4EHE9om/o6ujTSF4z++QFmUtJDwkV0wcYsazHktoiua0PIC8DgpBfA76O7AkL/1tE9J6xyFzJEAo4HpLentxHxKnctxDq6t5jr3IXAOaQRlH+G8TcittH9vltlrgMAHtQ29UV03e2zzKXghciDBBw7pJGD9rFabj7kLgJOIc2q/iuMoPDNxzBbC8BApe9Jt+GFzNAYaf2BgCNJXzhuQxo5ZLo5KFbqDLsPNWZ5q2FAVtG9ffF7DYBBSl0bt+GlzJDdRfc9afIvSgQcEdE29YfoOjcog24OimCvBk9oows2FrkLAYCH6Noojm6OmHjAkb58fAm7Nkq0iu4HeJu5DvjOTqjxr/C7hYctwswsAAOmu71oN9Vy8y53EblMNuDQajUKUkoGQajBnmw9B2DwdLePwjoi3k7xZfAkA462qT+FRaJjYmSF3gk1OEAbEZ/9ngJgyIykjM4kXwZPKuBIP7RfImKeuRROz2Idzk6owRHuouva2OYuBAAek5ah34bnmzGa1MvgyQQcfmgnYbKtWJxP+t1xERH/DL8/2N82utB1lbkOAHhS2rfxJYzuj9kiJrL/axIBR/qC8jX80E5BGxFvnFzkJdIH/T/DSVcOZxwFgGK0TX0Z3Utgxm8d3fekUYccow840g/tpxBuTEkbXSfHKnchlCGNr11ExK/pz35fcIxFdG2g28x1AMCzLBOdpNF3vI864JBITt5VtdwschfBMKXOrnl0nRrzrMVQOtdRAChK29S3EXGZuw6yGHXH+2gDDuEGiZCDiPi7S2Me3wKNWcZyGIc2umBjkbsQANiXcIMYccgxyoBDuMEPhBwTpUuDM7qJbhxl1HOsAIyLcIMdoww5RhdwCDd4hJBjAtIZ13nYpcH5rKL7fbLNXAcAHES4wQNGF3KMKuAQbvAMIcfI7Iyd/Jr+7Iwr57INZ18BKJRwgyeMKuQYTcAh3GBPQo6CCTTIwJ4NAIom3GAPowk5RhFwtE09j4ivueugCKP54Z0CgQaZfYyIG3s2AChV29TXEfEpdx0UYR3d96Sin3uKDzjSEsGvYdae/Qk5Birt0HgVAg3yWkS3QHSbuQ4AOJoOd45QfMhRdMCR3u7+FcINDreNiNcl//COwc6Vk3+E063kt4puHEX4CUDR0jPWn7nroEh31XLzNncRxyo24Ejhxtfwhpfjravl5nXuIqZipztjt0MDhmAbFogCMBLpmevP8BKY491Uy8273EUco+SAw7IcTmFRLTdXuYsYmxRA7nZnvArdGQzPNrpRlEXmOgDgJLwE5oSKPM5QZMBhWQ4nVuQP71AIMyhQGxGfq+XmQ+5CAOCUvATmhIrcW1hcwGGejDMo8oc3hx/GTIQZlKaNiM/hMgoAI+QlMGewjcL2FhYVcKQ3xX+GL1Sc3jYK++E9p52ujFcR8d/xrUMDSrWIboGon3EARsdLYM6oqKWj/yd3AQe6DeEG5zGLLvGe1D6O1JExi++DjFdhKRXjsQgnXwEYsfRi6kvuOhiti7apL0sZ6S+mg8MdZ3rytlpu7nIXcUo73Riz9Mc/ogsw5tmKgvNbhGADgAmwd4MetNF1u29zF/KcIgIOp47oURsRv5TWxp7aEu+DjCqEGEzXXXSjKNvchQDAubVNfRG6N+jHulpuXucu4jmljKjchnCDflTR/f02mDmznQ6MiG+BxX2AYZwEOqvoOjZWmesAgF6kZ0Qd7vTlVdvUH4Z+hW7wHRy2AZPJ2UdV2qae7/zT+3/8X/EtzBBewPNWIdgAYIKMppDB4EdVBh1wpFTyr/Alj/49OaryQzhxbxY/L8H99Zn/O3CcVQg2AJio9Cz6NXcdTNKqWm7e5C7iMUMPOKSS/P/s3U+SFEe2N+yja3f+5V1Bp8xy3mgFSlbQMM2JihUAKwBWAKygSpOcUr0CUitQaZ5myl7BjbuC9xuEJxRQ/yszjkf485hhUqsRcQRVkR6/OH4c4LJNCDYAaFy3WvwdXpyR50Wtp6pUG3BIJQG45CwifhdsANC6brV4GxFvksugbdUezFDzkFFzNwA4C8e9AkBEfDld8mV2HTRvFhGvIuJtch0/+K/sAq7SrRYn8XXQIgDtOYv+zcAL4QYAfPEmzCekDm9K4FaV6jo4ymBRLVcA7eki4jx0bADAD7rV4kmYT0hd3kTEi+wiLquxg+NVGJgD0JIuIt6Fjg0AuIkt/NTm5JrTJdNU1cFRujfsKQNowy4iPkbEWY1DqgCgFuUhcplcBlzlTfSn3FWhqoAj+u4Ne8oApm0X/TaUs+Q6AGAsbOGnVstutVjWctJdNQGH7g2AydtEH2xskusAgNHQvcEIVNPFUdMMDt0bANN0FhG/zNbbp8INALg3L4Gp3bKWE1Wq6eCIiN+yCwDgYLr4Ol9jl1wLAIxSeWh8ll0H3EEVJ6pU0cHRrRYn4eQUgCnYRf/h9vNsvX0r3ACARzF7g7E4qaGLo5YODm1XAOO2iYiPs/X2PLsQAJiCMqNQ9wZjchIRbzMLSO/g6FaLJxHxJLuOidpkFwBMWhf9fI2fy3wN4QYAHM5JmFF4DF1EXGQXMVHpjQs1dHCk/yaM2EX0IcZ/yt9fzNbb7rqfXFqG5tEHSv+IfhqzcAm4r118na9x7T0HAHgUz0kPt4mIP6Jfs+xuG3J+6TlpGV+fk+bHKm7CZt1qcTJbb8+yCvgp68IRX9qu/g7J5F11EXEe/Tfr+SEeLC61vv0rtMABNzuPfhvKJrsQAJiycjTs5+w6RmTfVfrHoTpKLw14/S28FL6P89l6+zzr4tkBx0lEnGbWMBIX0b8tPUiocZ1LYcebkFgCPaehAMDAutXiNPotKtzsPCJ+P/Y22RJ2nETfVePl/O1+zlo3Zgccn6Nv/+Fqm4h4l/G2tKTGb8KfD7RqE/2C4Sy5DgBoTrda/G94kL7JWfTPSbuhL1xe0nshfLPXs/X2Q8aF0wKOkoL9nXX9yu0i4kUNbeDdavEsIt6Hb2Bowb6986NuDQDIocv9RpvoH57Th4R2q8Xb0NFxnYvZevtLxoUzA45X0T848613s/X2bXYRl5WtK6/COdwwVZvQrQEAVehWi09hNt73uuhfAFd1Ylt5TjoNf15XSdmmkhlw/BmGtVx2Ef03bXoaeZ1ypO+n0M0BU6BbAwAq1K0W/y+7hspsIuJ5zSe3la6b96Gb47KUbSopAUdJuv4349qVOo8+3Kj2m3ZPSgmjN8gwLgDg/sr28E/ZdVSkuu7265SXwafhJf5eyjaV/xr6goWH46/ezdbbqhPJy2brbVeO/UkZGgM8yC4iXkffKvhcuAEA1fpXdgGV2G9JeZtdyF2VTvyn0XecEPGkzN0c1H8PfcHCN27vxVj3vM/W29fdavFXGIAEteqi79b4WPPWNwDgG8vsAirQRcTTMa5fykvrp475/WIZ/ZbowWRtUXHs0YjDjctMeYbqnEfEv6dwfwGAlpQtDn9m15FstOHG94QcERFxXrr/BzP4FpXyjSvcmMjDR/nveJddBzTuIvotKP9TtqCcJdcDANzfMruACkwi3IiImK23L6J/8dSy5dAXzJjBsUy4Zk3OpvbwUfbGnSWXAa3ZRT8L5+fZevvLbL39MJZZPgDAlX7NLiBZ1SdKPtCL6F9EtWpWGhwGkxFwtPyNuylJ3uSU/66Wv3lhCF30ocYvs/X259l6+9oRrwAwGcvsAhJ9mNpL4IgvMzmeR7+Ga9VyyItlDBldJlyzBvsv7il7Hv2+wda3IMEh7YeF/tvpJwAwTY1v49/M1tvX2UUcy2y93XWrxYto9/jffw55sUE7OMoxMa1+476Yevt4eZM8yQ4VGFgX/bav57P19n9m6+0L4QYATNqgbfwV6aKB54eyjvuQXUeS5ZAXG3qLSqvfuOetPJyU/85Ndh0wQkINAGjXoG+5K/Kuoe2276KfodaaebdaDNbkMPQWlRYDji760w1a8iIi/s4uAkbA9hMAIKLN56SL2XrbTFfDbL3tutXiXUScZteS4EkM9BJ86A6OFgeMfmwolYyIL1tVHB0LV9OpAQB8b5ldQILWXgJHGaS6SS4jw3KoC+ngOK79iQct+hARL6PdmStw2S6+dmpscksBAGpS5hS2ZtPwmuhdtBdo/WOoCw0dcLT2sPtx6oNFr1NasM4i4lV2LZDkIiL+Hf0MHkcoAwDXmWcXkKDZbu/ZervpVouLaOvl/3yoCw0WcHSrxXKoa1XkLLuAZB9DwEFbziPij+hDjV1yLQDAOCyzCxjYruHujb2P0dYsjuVQFxqyg6O17o3mH3DKmc/nEfEsuxY4kl30+yj/HX2rZZMdWwDAo/x/2QUM7GN2Adlm6+1Zt1q8j/aekY9uyICjpRaciIjfswuoxL9DwMG02HoCABxSa89JZ9kFVOI8Ik6yixhKt1osh+jcGTLgaCmZ7JyK8MV5tNV+xfTsou/S2G890aUBAPAw1lJf/R4NBRxD0cFxHJvsAmpRho3apsLYbOLrthNdGgDAMS2zCxjQH9kF1KIMG+2inW0qyxjgOXnoU1Ra4Rv3W3+EgIO6XUTp0tB9BQBwNJvsAiqzCc9JBzVkwDEf8FrZNtkFVMYbcGqzC8NBAYBk3WrRytv7iH4bv+eCb7X0IniQkRUCjiPwjfut0n6VXQZt28XXORqb1k84AgCq0dI2fs9IP2rp92SQr3VbVA5vk11ApS6irRs4uXYh0AAAqIlt/D9qKeAYhIDj8HbZBVTKFgCO6csMjYi4EGgAAFTH88B3yoEMLQ0aPToBx+H9J7uASv0RbU2I5rg2Ubozog80fGACANRNt8LVLsJz0sEMEnB0q4WtCcBDXZQff4VjWwGAaZlnFwBTMlQHR0stN7vsAmDEuui7Mv4K3RkAwPTNswsYkDVd2wwZHalddgGVckPje130nRl/lL+anQEAMFG6cK/VyhaVQZoeBBwMxQ2tbbvyQ5gBAABf/V92AVMi4AAObRN9mPGfsM0EAOAmu+wCYEoEHMBDXUT/ofzX/u+1HgIA3MsuuwCYEgEHcJtN9PMyBBkAADyEbl4GIeBgELP1dtOtFtllcL1dfJ2RsR/+uTMnAwDgqFp5adTKfyfJBBwMaRdtHYVVm035qxADAKACs/W261aLLgY6YSLRLrsA2iDgYEgXIeA4ll35sd9KElECjdl6u8koCACAO9lExLPsIo7sj+wCaIOAgyH9EdO/eR/aLr4m3hfRHyO1776IcEIJAMDYtbBGtkWFQQg4GNJ5RLzPLiLRRXw7YOlykr2Lr0GGbSMAAO3YZBdwZAbUMxgBB4OZrbe7brXYxXS3qZxFxO/l7zs3cgAAbjNbby8mvkY+zy6Adgg4GNrHmG4Xx0ehBgAAD/B7RLzJLuJIfr/9p8Bh/Fd2ATRnqgnuhXADAIAHOssu4EiskRmUgINBldkSZ8llHMPH7AIAABgna2Q4DAEHGd5lF3Bgu9l6e5ZdBAAAo2aNDI8k4GBwE0yoX2cXAADAuFkjw+MJOMjyOr49MnWsNrP1dqpzRQAAGJY1MjyCgIMUs/W2i4gX2XU80hT+GwAAqERZI4+988EamTQCDtKUVPcsu45HeF1aCQEA4CDK3Ioxdz9YI5NGwEG21xExxqOjzgxNAgDgSF6ENTLcm4CDVKUN72mMa6/hZrbearsDAOAoyhr5eYxrjXxujUw2AQfpRhZyXET/YQMAAEdTtnmMaY0s3CCdgIMqzNbbi6j/Br6JiKclkAEAgKOyRob7EXBQjXID/znq3G94Nltv3bgBABhUWSP/EtbIcCsBB1W5tF3lLLmUvS4iXthPCABAlkvbVc5yK/nCGpkqCTiozmy97crNMnuw0ib6druzxBoAAMAaGe5AwEG1ZuvtefRbVt4NfOl9Iv20tAQCAEAVLq2RPwx86V1YI1M5AQdVK0n12+hv4mdx3LR6FxGvI+JniTQAALUqa+TXMewa+RdrZGr339kFwF2UfYcvutXidUQ8i4iXEfHkQL/8eUT8XtJwAAAYBWtk+JaAg1EpQ0jPIuKsWy1m0d/I/xn9jXx5h1+ii34C9R8RceGGDQDA2F2zRv41IuZhjUxDBByM1qUb+Te61WJ5xU/v7BUEAGDqLocd+39WQo+rOjuskZkUAQeTM1tvN9k1AABALUroscmuA47NkFEAAABg9AQcAAAAwOgJOAAAAIDRE3AAAAAAoyfgAAAAAEZPwAEAAACMnoADAAAAGD0BBwAAADB6Ag4AAABg9AQcAAAAwOgJOAAAAIDRE3AAAAAAoyfgOLxZdgEAAACMwq/ZBUyJgOPwnmQXAAAAABW5GOIigwQcs/V2M8R1AAAAgOp0Q1xEBwcAAAAwegKOw/tndgEAAACMwjK7gCkRcByeIaMAAAAwsCEDjkGGigAAAEDtutWipZfjfwxxkSEDjkGGilRgmV0AAAAA1XMC54HZogIAAACM3pABx27Aa6XqVgtJHAAAADdZZhcwoEFGVgwZcPxnwGtla2kvFQAAANxkkJEVtqgchw4OAAAAbvJrdgFT4xSV49DBAQAAwE2aeW6crbebIa7jFJXjkMQBAABwE53/BybgOI5mkjgAAADup1st5tk1DGg31IUGCzhm621LW1QkcQAAAFxnnl3AgHZDXWjoIaPNdHE4KhYAAIBrLLMLGNBuqAsNHXDo4gAAAKB1/8guYED/GepCOjiOZ55dAAAAAFVq6YX4bqgLDR1w/DXw9TI5SQUAAICrCDiOYOiAYzfw9TK19AULAADAHXSrxTK7hoENNqpCwHE8M4NGAQAA+E5Tz4mz9XawURWGjB5XU1+4AAAA3KqlcQabIS82aMBRkpuWBo229IULAADA7Vp6ET7o8//QHRwRbXVxLLMLAAAAoA7dajGPtk7cHPSgEQHHcc271WKWXQQAAABVWGYXMLBBn/8zAo7/JFwz07PsAgAAAKhCa2MMdkNeTAfH8bX2BQwAAMDVltkFDGm23k6+g6O1gGOZXQAAAAC5utXiSbQ1f2Mz9AUHDzjKSSq7oa+baF6+kAEAAGjXMruAge2GvmBGB0eELg4AAADa8q/sAgY26AkqEXkBx+D/ocl+yy4AAACAHOV0zWV2HQMbvLEhK+DYJF03yxPHxQIAADSrudM1Z+vtZuhr2qIynOa+oAEAAIiI9ranpDzzpwQcDQ4ajWjvCxoAAKB5pZu/tRfem4yLZnVwRLS3TeWZbSoAAADNaS3ciEiau5kZcLQ2aDQi4iS7AAAAAAbVYjf/JuOiOjiG5TQVAACARnSrxTza6+DYzdbbXcaF0wKO2Xp7ERFd1vWTPOlWiyfZRQAAADCI1sKNiMRDRTI7OCJ0cQAAADBdL7MLSPBH1oWzA460//BEJ4aNAgAATFu3WiwjYp5cRoZN1oWzA45N8vUztHhEEAAAQGta7N7flXEUKf4768IR/RyObrXoon/ob8nLiDjLLmLKSlr6jdl6uxm+EgAAqEMZeDn//h9nPpBOVfm9PkkuI8Mm8+KpAUdxHu39wT/pVoulB+7HuzSV+J8R8aT8uO7n7v/2ovz4KyLOsyb8AgDAsZQXfsv4uk6e3/BzI/oDIPbr5D8iYjNbb1s7FOKQTrILSJI6huKnzItHRHSrxUlEnGbXkWAzW2+fZhcxRiXUeBl9sDE/wC+5i4jfI+JM2AEAwFiVUOO36NfJh+iSP4+If0f/UlDYcUdl5uLf0d5OhYiInzOfqWoIOGYR8b/ZdSRJ/cMfm3LD3gcbx3IeER911wAAMBblpfGbON5Ayy76LfYfPb/crlst3kb/59Gai9l6+0tmAekBR0REt1r8GTdsLZiws9l6+yK7iNqVjo3T6FvshrKJiBdu4AAA1KpbLZ5FxPsY9qSOdxHxQUfH1Rrv3vgwW29fZxaQfYrK3r+zC0hyUh7euUZJP/+OYcONKNf7u1wfAACq0a0W8261+BwRn2L4Y0jfRL9OdjLk1V5Fm+FGRL/tP1UtHRxPIuLP7DqSnM/W2+fZRdSmfE2cRh2dPRfRd3OYLg0AQKoSLJxGHQ/RZxHxWjdHr/HujW623v5PdhFVdHCUB8dddh1Jnl11pGnLyk37c9QRbkT0dXyWUgMAkKlbLd5H37VRywP0SfTr5FrqydZy98Z5dgERlQQcRRW/IUlaHEBzpTIgqaab9t4sIj6V+gAAYFDdanEa/QN0bZ5Ev2WllpeTKUrI8zK7jkRVjJ2oKeBIPS832VJ3wGiODD4VcgAAMKQSbpxk13GDWfSdHC2HHO+jvpe0Q+lm620VDQvVBBzlN6TlvVvvW27tGkm4sSfkAABgECMIN/b2Icc8u5ChlWDnJLuORFWEGxEVBRxFNb8xCeZRZ8vZ0V0aKDomp40n1AAAHFk50e8kt4p72W/rbu3F7fvsApJVsT0lor6AI/1YmWRvWks8y83vc3YdD2SgEgAAR1G2sI9xVt8YX14+WLdavIqIZXYdiarZnhJRWcAxW2830e5pKnvN3AyKWo64eohZtPfnBQDAkZWXaGNeZz4rD/6TVl5OjzGEOqRqwo2IygKOoqrfoATLFm4GEV9S6bEPV31mQCwAAAc25peAe28a6HZuebDoXjXbUyLqDDha36YS0d8MJj3fYQKp9GVND4gFAOBwutViGeN/CRjRP/hPdjZFeSk9hT+nx9jVtD0losKAY7beXkTERXYdyab08H+dVzGdtHMejQ6IBQDg4Ka05eFkijMGbU35oqpwI6LCgKPQxRHxpFstJpl4lm6Hl9l1HNhLXRwAADxG6d5YJpdxaFMMAj7FdF7WPsbH7AK+V2vAcZZdQCVeTXS+w0lM74YwCy1qAAA8zhTDgEl1cZSX0JMeJ3BHm9l6u8su4ntVBhyz9bYLIcfe6QTncUyte2Nvih9IAAAMoIQAy+QyjmUS6/9utTgJW9P3qtx1UWXAUVT5G5ZgFn3IMYmOhxLWzLPrOJL5BMMoAACGMYkQ4Bqj73Qu6/xJjhB4gC4qnL8RUXHAMVtvNxGxSy6jFk+i3+c1Bb9lF3Bko795AwCQYpldwBGN+kVgedn8Oaa3zf6hzsqui+pUG3AU77ILqMiyWy2mcLLKMruAI/tXdgEAAIxL2Z4y2gDgjkb5IlC4caXqhovu1R5wnEff/kLvpFst3mYX8VDl5jD1G/fU//sAADi8ZXYBA/g1u4AH+hzW+JdVOVx0r+qAw7DRK70pw23GqIkbQzneCwAA7uqf2QUMYHTPAqWDfnR1H1nVuyyqDjiKattfEp2ONORo5ebQyn8nAACH0cL6cTam42JLuHGSXUdldmVWZrWqDzhK+8tZchk1GmPI0cq+tVb+OwEAOIx5dgEDmWcXcBfCjWtV3b0RMYKAo3Bk7NVOu9ViTOcwj3Xf3X210GIIAMDhzLMLoJ8Z2K0Wn0K4cZVutt6eZRdxm1EEHKUNZpNcRq3eT+R0lSnRwQEAAD9aZhdwnUunpYzytJcBjGJ0xCgCjqL6dphEJ91q8al8UwIAAHBH3WrxJJyWcpMuIj5kF3EXowk4dHHc6llEfB7T4B4AAIBM5QRE4cbNPpYTTqs3moCj0MVxsycR8adjSgEAAG7WrRZvow83dMJfbzTdGxEjCzh0cdzJLPpOjrfJdQAAAFTn0jDRN9m1jMBoujciRhZwFLo47uZNt1rYsgIAAFCUbve/wzDRuxhV90bECAMOXRz3sox+y8qYjpIFAAA4qNK18T5sSbmPUXVvRIww4Ch0cdzdLPqjZHVzAAAAzSldG39GhBe/d7eLkXVvRIw04ChdHOfZdYzMMiL+7laLt46TBQCAOngJeTzdajEvszY+R8Q8uZyxeTe27o2IkQYcxevsAkbqTfTbVk6yCwEAAJp68N4McZGyHeVt9F0bZm3c32623p5lF/EQow04ZuvtLkbYMlOJeUSclm0ry+RaAAAADqK8yP0z+he7Otcf5kV2AQ812oCjeBf9ZFceZhn9kbKCjsN6kl0AAAC0pFstTrrV4u+IOI22umIObVNGQozSqAOOsifIVpXHW8bXoOOYLVx/HPHXromkGAAAjqxsRXkl2Dio0XZvRET8d3YBjzVbb8+61eJleGt+CMuIWHarxS4iPkbE2RgHywAAANNVBrO+jIiT8HLxkN6VURCjNeoOjktGnTJVaB4R76M/deW0Wy2ERwAAQKputXhWTkX5O/ojX4Ubh7OLCcy4HH0HR0TEbL296FaLD+Fc40ObRZ+Knlzq6jgfe6oHAACMQ3nZ+lvo1ji211Po3p9EwFG8i/4IoHlyHVM1j76r4323WlxExO8h7AAAgMfSLf2dS6GG57thnM/W2/PsIg5hMgHHbL3tutXidUR8yq6lAU/Kj/els+M8Iv6YyjfFIXSrxWwKCSgAAEfXUlfCxVX/sFstZtGHGb+Wv7b0e5KtiwmNfJhMwBERMVtvz7vV4jz6bwqGMY9+a9CrbrWIiNhEf1rKJiIuGn7IfxL97wEAABBfTsHcBxrL6AONZehiyfRiSs9skwqbCIfTAAAgAElEQVQ4ihfRf5NI/XIsy483ERFlO8tFRPwVfToIAAA0qAwIfRK2ndRiMltT9iYXcJStKi/CVpVa7LezAAAAbdNpX49JbU3Zm8oxsd8oKdToj7gBAAAm75/ZBdCkSW1N2ZtkwFG8i/4sX8igawUAgLuwtZ6hnU1ta8reZAOOkkY9z66DZvmgAgAAarOLiNfZRRzLZAOOiIjZensRE/7DAwAAgHt4PsWtKXuTDjgiImbr7YeImGT7DQAAMHrL7AJoxuvSBDBZkw84ihdhHgfD+jW7AAAAgOKsvPyftCYCjkvzOCbbigMAAABXaGZ0QxMBR4R5HAAAQF261WKeXQOT18VEj4S9SjMBR0TEbL09i4jJt+VQBcfEAgBwm3l2AUzei6nP3bisqYAjImK23r4OQ0c5PsfEAgAAmV7P1tumnn2bCziKF9HvQwIAAMgyzy6AyWpiqOj3mgw4DB1lCN1qscyuAQCAqs2zC2CSNrP19kV2ERmaDDgiImbr7S4inoaQAwAAgGm4iP5lfpOaDTgivpys0uwfPkdnDgcAADf5NbsAJmUXEU9bOTHlKk0HHBERs/V2E/1MDjg0J6kAAABD6CLiecvhRoSAIyK+HB8r5AAAAIY0zy6ASeii79xo/iANAUch5OAI/pldAAAAVZtnF8DoCTcuEXBcIuTgwMzgAADgSt1qYa3IYwk3viPg+I6QgwOaZxcAAEC1zGvjMYQbVxBwXEHIwYHMswsAAAAmR7hxDQHHNYQcHEK3WsyzawAAoErL7AIYJeHGDQQcNxBycADz7AIAAIBJEG7cQsBxi0shR9PnCfNg8+wCAACo0q/ZBTAqFyHcuJWA4w5KyPE0hBzc3zy7AAAAYNSEG3ck4Lij8sX0NCJ2yaUwLv/ILgAAgCotswtgFM6jDze8bL8DAcc9lJDjl+gTNLiLeXYBAADUpVstZtk1MApns/X2uXDj7gQc9zRbb7vZevtLRJxl18IoON8cAIDvWSNymxez9daBF/ck4Hig8sX2OrsOqiedBwDge/PsAqhWFxG/lDmQ3JOA4xFm6+2HMHyUW3SrxTK7BgAAqjLPLoAqXUQfbhiJ8EACjkearbebMJeDm+niAADgsn9mF0B1zmbr7S+z9XaXXciYCTgOYLbe7spcjg/ZtVAleywBALjMCzD2ujBv42AEHAc0W29fR8TzsGWFbzkqFgCAy5bZBVCF/ZaUs+xCpkLAcWCz9fY8In6OiE1yKdRjnl0AAAB16FaLeXYNVOGdLSmHJ+A4gnKU7NPoT1nRzcEyuwAAAKoxzy6AVLuIeDpbb98m1zFJAo4jKqes/BK6OZonqQcAoFhmF0CaD9FvSdlkFzJV/51dwNSVlqOn3WrxKiLehIFCrZpHn9YCANA289nas4t+kOgmuY7J08ExkEvdHOfZtZBimV0AAABVmGcXwKDeha6NwejgGFDp5njerRbPIuJ9uLm1RFIPAECEF1+t2ETftbFLrqMpOjgSlJNWfok+zaMNT7ILAAAgl7lsTdhFxPPZevtUuDE8AUeSctLK2+iPlD3LrYYBCDgAALAmnK4uvm5HMZYgiYAj2Wy93c3W2xcR8TSctjJp3WrhAw0AoG3Wg9N0Fn2w8Xa23nbZxbTMDI5KlKEzm261WEbEaZjPMUVPIuIiuwgAANL8M7sADuo8Il7bilIPAUdlStDxc7danER/rOw8sx4Oap5dAAAAqXRwTMMmIt45GaU+P2UXwM0EHZOyma23T7OLAABgeN1qMYuI/82ug0c5j4iPgo16mcFRudl6ezZbb3+OiOdhRsfYSewBANplLTheZxHx82y9fS7cqJsOjpEpMzp+i4iT3Ep4oJ/t0QMAaE+3WryNvjObceiiDzY+Wr+PhxkcI3NpGOm7iHgZfdAxy6yJe3kS/dnYAAC0xYDRcdhFf9zruRNRxkfAMVIlRXwdEa/LnI7fImKZWBJ38yT6vXsAALTFFpW6nUXE77agjJstKhPSrRbz0NVRO4NGAQAaY8BotS4i4veIONOtMQ0CjonqVov/l10DV5utt77vAAAaUubofc6ug2/8MltvL7KL4LCcogID61YL7YkAAG1ZZhfAt4Qb0yTgmK5ddgFcS8ABANCWX7ML4Bu77AI4DgHHdO2yC+BaPuAAANriBVdddtkFcBwCDhjeMrsAAACGUbYnOwAABiDgmC57yuo1L5O0AQCYPt0b9fGsNFECjun6v+wCuNEyuwAAAAZhe3J9PCtNlIADcvigAwBowzK7AGiFgGO6tF3VTasiAMDEdavFPCLmyWXwI89KEyXgmK4uuwButMwuAACAo/NSq06elSZKwAFJutVimV0DAABHZVsyDEjAAXmW2QUAAHBUy+wCuJIOjokScEzUbL3dZNfArST6AAAT1a0Ws7BFpUqz9dYMjokScECeZXYBAAAczbPsAqA1Ag5I1K0WPvgAAKZJty4MTMABuXzwAQBM0zK7AGiNgGPa7C2r3zK7AAAADqtbLeYRMU8ug6t5RpowAce0mQ5cvydlABUAANOxzC6Aa3lGmjABB+QzhwMAYFr+lV0AtEjAAfnM4QAAmJZldgHQIgEH5NPBAQAwEd1q8SQibEGGBAIOyDcrH4QAAIyfl1eQRMAxbSYEj8cyuwAAAA7C/I26eUaaMAHHtP1fdgHc2W/ZBQAA8DjldDyduXXzjDRhAg6og+NiAQDGz/YUSCTggHr4QAQAGDfbUyCRgAPq4bhYAIBxW2YXAC0TcEA9dHAAAIxUt1o8C8fDQioBB9RjVj4YAQAYH9tTIJmAA+pimwoAwDgtswuA1gk4oC46OAAARqZbLZ5ExDy7DmidgAPqMi8fkAAAjMdv2QUAAg6okQ9IAIBx0YULFRBwQH18QAIAjITtKVAPAQfUxzYVAIDx0H0LlRBwQJ18UAIAjIPuW6iEgAPq5IMSAKBytqdAXQQcUCfbVAAA6qfrFioi4IB6+cAEAKibrluoiIAD6uUDEwCgUranQH0EHFAv21QAAOql2xYqI+CAur3MLgAAgCvptoXKCDim7dfsAng0H5wAAJXpVotnYXvKWHlGmjABB9RtVj5AAQCox7+yCwB+JOCA+tnfCQBQiW61mIUuW6iSgAPq96x8kAIAkO9ZRFibQYUEHDAO3hIAANTB9hSolIBj2iTL0+E0FQCAZN1qMQ8vnqBaAo5pe5JdAAfzpFst/HkCAOQ6yS6AR1tmF8DxCDhgPAwbBQDIZT0GFRNwwHicZBcAANCqbrVYRsQ8uQzgBgIOGI9Zt1qcZBcBANAo3RtQOQHHRJWEmenxwQoAMLButZiFbtrJ8Kw0XQIOGJdlmd4NAMBwTrILAG4n4IDxcWQsAMCwrL9gBAQc0zXPLoCjOckuAACgFYaLTtI8uwCOQ8AxXfPsAjgaw0YBAIZjBtr0zLML4DgEHDBOPmgBAI6szD47SS4DuCMBx3T9I7sAjmrZrRZPsosAAJi4k+wCOArPShMl4JiueXYBHJ1hVwAAx6Vrdprm2QVwHAIOpuAiu4Akz8qZ7AAAHFiZeTZPLgO4BwHHdLWyfeEiIn7PLiLJLLRNAgAci+6N6WrlWak5Ao7pauXNfhcRZ9lFJLJNBQDgwMrRsMvkMjieVp6VmiPgmKAy7bkZs/W2i4jz7DqSzB0ZCwBwcLo3Jq61Z6ZWCDimaZ5dwID28zf+nVpFLh/AAAAH4mjYZsyzC+DwBBzT1FLL1f9FRMzW27Pot6u0aFnaKAEAeLyT7AIYREvPTM0QcExTS0NzLocarW5TidDFAQDwaOWEupZnnG2yCxhQS89MzRBwMHaXj4ht9TSViIgT+wgBAB7tJNp9s99FxLvsIuAxBBzT9Gt2ARlm6+0mInbJZWR6k10AAMDItdy9cZZdwMCafGaaOgHHNLWUOl98978/plRRh5PSVgkAwD2Vk+nmyWVk+j1+XFtPmXXzBAk4pqmZ/WTliNjLWp7DERHxKrsAAICRarkb9mK23l5csbaesmaemVoi4JiYxt7g777/B7P1dhdthxwvG/saAAB4tG61eBZtd29c7oLeZRUxNOvm6RFwTE9LSeTumn/e8rDRWejiAAC4r5Znb0R8+4Jwl1VEgpaenZog4JieeXYBA7qyhW623p5f9/81ovUPaACAO+tWi2VELJPLyHT23daUltbR8+wCOCwBx/TMswsY0F83/H9nQxVRoVkZkgUAwO1anr0R8WP3801r7KmZZxfAYQk4puef2QUM6KZ0ueXTVCJ8UAMA3Er3Ruxm6+3mu3/WUgdHS89OTRBwTM88u4ABXXuMVRk2uhmskvrMdXEAANyq9ZdCV70UbOmo2Hl2ARyWgGN6WhqUc1u6rIsDAIAr6d6ILq7e1t1SB0dLz05NEHBMSLdaNPUNOltvb0yXy7DR3TDVVEkXBwDA9Vp/GXT+3XDRiLh9jT01rT1DTZ2AY1rm2QUMaHfHn9fykbERPrgBAH6geyMibu523g1VRAXm2QVwOAKOaWkpfdzd8eedHbGGMdDFAQDwo9ZfAl3c0qmxG6qQCrT0DDV5Ao5paWkK8J1a58qw0bOjVlK/1j/AAQC+0L0REbfPqtsNUUQlWnqGmjwBx7S0lD7+3z1+buvbVObdavE2uwgAgEq0/vKnm623Z7f8nP8MUUglWnqGmjwBx7TMswsY0OauP7Gc7d3UsKQrvOxWi1l2EQAAmXRvRMTdThrcHLuIisyzC+BwBBwTUW7WLbnv8VWtHxk7i4hX2UUAACRrvXsj4m7bt1s6KrbFZ6nJEnBMR1OtVfc9vqq04TV1o76CLg4AoFll8PoyuYxsZ2VG3Y1aOyo2GnuWmjIBx3S0NBznoTdcXRy6OACAduneuN9supZCjpaepSZNwDEdLaWOuwf+e2cHrGGs3nSrxTy7CACAIZXujXlyGdk2ZTbdXe2OVEeNWnqWmjQBx3S09E3510P+JUfGfuHtBQDQjLJF9312HRW478mCD1pzj1RLz1KTJuCYgAaH4jymXa71bSoRESfdauEmDgC04lX0W3VbtrvD0bDfa2mLSovPVJMk4JiGZXYBA9s99F8sA5M2B6tkvLzFAAAmr2zNfZldRwUe8pJvd+giKrfMLoDHE3BMQ1NDcQ4w1fndQQoZt6WUGgBowJvQvdHFA7ZpN3iSSlPPVFMl4JiGZXYBA3r0jbYMV9o9upLxO80uAADgWMqW3JPsOirwcbbedg/8d1sKOZbZBfB4Ao6RK213LaXSh7rJ6uKImHerhWNjAYCpsiW3d/aIf7elgGPmtMHxE3CM3zK7gIH95xC/SBmytDvErzVyb8pkcQCAyehWi2fR3jr5KmflJMGHOsjae0SW2QXwOAKO8fs1u4CBbQ74azlRpe/+cWwsADA1ujd6j+1a3hyiiBFp7dlqcgQc47fMLmBgh2yTO4t+6FLrXjk2FgCYim61eBsR89wqqvDY7o2ItraoRLT3bDU5Ao4RK3vE5sllDGn3iAFJPyi/li6OnrccAMDoORb2G49e55b18u7xpYzG3ByOcRNwjNsyu4CBHSNB/hC6OCL6Y2OfZRcBAPBIjoXtbQ54zKsuDkZDwDFure0R++vQv6Aujm+8N3AUABirbrVYhmNh9w55YuDB1+CVa+0Za1IEHOPW2hv3zZF+XV0cvXlEODYWABgrW257m9l6uznkr3fAX2sMltkF8HACjpEqQyFbe9t+lPY4XRzfeGPfIQAwNt1q8SoiDE3vHbJ7I6K9LSpzA/jHS8AxXsvsAgZ2ccgBo1c4O+KvPTan2QUAANxVeTnj2Pveobs39i8DWws5ltkF8DACjvH6V3YBAzvqTbUcoXV2zGuMiIGjAMCYvI/2Opuvc+jujb3WAo7WnrUmQ8AxQmUQ5DK7joH9McA1jvWBMEanBo4CALUrg0W9mOkdvHvjkiHW4jVZWguPk4BjnJbZBSQ4emqsi+Mbs9DqCQBUrDyA2lr71TFf1rXWwRHR5jPX6Ak4xqm1lqnugOd43+Z1OFFl71V5KwIAUKM30Z8Cx3G7N6KsxVtbI7f2zDUJAo5xaq0NbzPUhZyo8gNvRQCA6pRTLhxv/9UQW603A1yjJq09c02CgGNkGj0e9q+Br/ch2kuorzPvVou32UUAAHzHS5ivjtq9ccnQa/JsM8fFjo+AY3x+yy4gwWbIi+ni+MEbN3cAoBbl5Yu1yVdDDcrfDHSdmrT47DVqAo7xaa5VaqBE+nu6OL7lLQkAkK5bLeYR8TK7jooM1b2RtSbP1tyz19gJOEakvEWfZ9cxsE3GRXVx/OBJt1rY5woAZDuN9rZr3+TFwNfbDHy9bHOdzOMi4BiXFlukMs/c1sXxrTflrQkAwODKy5Zldh0VOZutt7uBr5m5Ns/S4jPYaAk4xqXFFqlN1oVLF8frrOtXyFnzAECK8pLlTXYdlRlq9sZlm4RrZmvxGWy0BBwj0ej2lC57r99svT2LiF1mDZVZ2qoCACSwNeVbHxK6N/ZzOFrrcLZNZUQEHOPRYmvUJruAIiMdr5lTVQCAwdia8oMuctenm8RrZ2nxWWyUBBzjcZJdQIIq9viVLo5Nchk1sVUFABhEealia8q3Ppat1FmqWKMP7CS7AO5GwDEC3WrxLNpsyTvPLuASXRzfelLOoAcAOCZbU761i34Qfqaa1uhDmZVnMion4BiHf2UXkGCXsa/wOmW/YYs385vYqgIAHE15mWKt8a13yd0bUdbou8wakrT4TDY6Ao7KdavFLNpsidpkF3AFJ6r86LR8jQIAHIytKVe6KFuna7DJLiDBiXVv/QQc9TvJLiDJv7ML+F5Jq7NbAmtj8QEAHFR5iPyUXUeFanrZVt1afSAn2QVwMwFH/Vqd2LvJLuAa76K9o7Fu88qeRADggN5HxDy7iMpsypbpWmyyC0jS6rPZaAg4KlZa81rcd3ievbfwOqUuA0d/ZKsKAPBo5aXJSXYdFXqRXcBlZU3c4ny6J2bQ1U3AUbeX2QUkqfroqdl6+yHaHKx0E62kAMCjdKvFPBxFf5UPNQ3fv6TqNfsRtfqMNgoCjko1PFw0YhxpcFUpeiWW3WrxKrsIAGC0HAn7o5q7h8ewZj8Gw0YrJuCo10l2AUkuKk2ov1H2QG6Sy6jRe217AMB9lSNhl7lVVCn9WNjrlDX7RXYdSU6yC+BqAo56tdr6NKaJzLo4rvZJqg0A3FW3WizDqWxXuShbo2u2yS4gSavPatUTcFSoDFeaZ9eRZDStbiW1rrVlMNM87J8FAO7AkbA3qulY2Ov8nl1AkrlTBOsk4KhTq4ngbrbejq3N7UM4NvYqz8zjAADu4FOYu3GV88qOhb1SWbvvsutI0uozW9UEHJUpLXrL5DKyjKZ7Y6/siRxDup7BPA4A4FrmblxrbOvL0a3hD2RZnt2oiICjPr9lF5BolC1us/X2LNrdf3gb8zgAgB+Yu3Gjj2MYun/JKNfwB9Lys1uVBBwVKWd/nySXkWWM21MuG1PKPqR5mMcBAFxi7saNdrP19m12EffR+DaVk/IMRyUEHHVpOcUedWtbubHXPuU6y7PSggoAEBHxOczduM5YT+kb9Vr+kVp+hquOgKMSjXdvREyjte1dtJte3+aNPYoAQLdavI8IM7qudjaGwaLXmMJa/qF0cVREwFGPlpO/sW9PiQgDR+/gk5s/ALSrWy1OIsIpa1cb9Tqy8W0qEW0/y1VFwFEB3RvTSXxn6+15GDh6nVkYOgoATSonq73PrqNi78rLsjGbzJr+AXRxVELAUYfWE7+z7AIO7EX0KTw/srgBgMZcGirqJcfVNrP1dgqz3M6yC0jW+jNdFQQcyXRvxMXIjsG6VfnveZddR8VOutVCeyoAtONT9CercbXRbk25rKyBR7/t/BF0cVRAwJGv9aTvY3YBx1BS+JZv8Ld5b+goAExfGSq6zK6jYu+mMIvukkmu7e+h9We7dAKORGUv4kl2HcmmfKTUWI/5GoqhowAwYYaK3mo3W2/fZhdxYFNe29/FSXnGI4mAI1frswjOJjBM6VoljbdV5XqGjgLARJWHvNPsOio3uZdhZW1/ll1Hstaf8VIJOJKU9vxlchnZ/p1dwAA+RNtHZt3G4gcAJqZ0aH7OrqNyH2br7Sa7iCNpYY1/k6Wt2HkEHHlaf6jblSNVJ62k2JNL5w/sWdmfCwCMnBNT7mQXE+7yLWv8XXYdyVp/1ksj4EhQTpCYZ9eRrJlzsks6P4Wjv47pVdmnCwCM22n0HZpc78WUt2kXzaz1rzF3amAOAcfASqptum57e/PehST7Nqfa+QBgvLrV4jQinmXXUbmzCW9Nuewsu4AKvDFrbngCjuG9Dy175+Wc7GbYqnJnn0yeBoDxKZ2YJ8ll1K6LiNfZRQyhrPUnvx39FrMwcHRwAo4BlbfTJ8ll1KDJljVbVe7EySoAMDLdavEszBy4ixa2plzW5Jr/Oyc6lIcl4BiWBK+R4aI3sFXldvOI+CzkAID6OQ72zj60tgY2bPQLz4ADEnAMpFst3oaBSxERH7MLyGSryp1ZLAFA5S4dB+ulxM12MeFTU27R9Nq/eFKeBRmAgGMA5eb/MruOCnRh4JCtKnf3rAwrAwAq4zjYe2lta8plZ9E/A7TuZXkm5MgEHMM4DTf/iH64qBtc711EXGQXMQInEm8AqEsJNz6H7uS7+NDIqSlXKmv/prbmXGMWupMHIeA4snL+8TK7jkq02pr3A1tV7uVNmcwOANThNIQbd3ER1r8Rfg/2luXZkCMScBxRaUN6k11HJTatHQ17m9l660Pv7k6FHACQr2wffZZdx0i0vDXli/IMsEkuoxZvbFU5LgHHcdma8pUH+SvM1tu3YavKXZ2WSe0AQIISbpxk1zES78rLLHqeBXq2qhyZgONIytyAZW4V1bhoee/hHTwPw5fu6rOQAwCGVzopT5LLGItNeYlFUZ4FBD69pRlzxyPgOILyAGZryleOh7pBadt7nV3HSMxCyAEAgyrhhrfOd2PO2vU8E3z1xnr2OH7KLmBqylTpPyNinlxKLXaz9fbn7CLGoFstPoU9rXfVRcRTrZ8AcFzCjXt7MVtvz7KLqFW3WvwdnpP2dhHxizkth6WD4/BOwzftZfbb3d2L6G903G4W/UwOM24A4EiEG/d2Jty4lWeDr+bh++vgBBwHVI798Qb+q52b/N05OvbenkS/XUXIAQAHVtrnPXzd3S5sOb5VeTbYJZdRk2eOjj0sAceBlA+B99l1VOb37ALGpgxgkmzfnZADAA6srGs/Z9cxMs9tNbgzzwjfem8ex+EIOA6gPFx9yq6jMl1EfMguYozK1O1NchljIuQAgAO5FG74XL271+aC3cuHcILg9z5Zyx6GgOMwPoW5G9/7KMV+FEfH3o+QAwAeSbjxIOez9dZLvXsozwhOVPnWPLwwPwgBxyN1q8VpRCyz66iM7o1HKjf+59l1jIyQAwAeSLjxILswP+2hdHH8aFmeLXkEAccjlMnSJ8ll1Ej3xgGUeRyCovsRcgDAPQk3HszcjQfSxXGtk/KMyQMJOB6oWy2WYbL0VXbhofxgZuvt6zCP476EHABwR8KNBzN34/E+hBNVrnJanjV5AAHHA5QPAnukrvZOkn1w5nHcn5ADAG4h3HgwczcOoDwzOD3wap+crPIwP2UXMDblgenv8EFwld1svf05u4gpKimu49ru7yIingrdAOBbwo0H20XEL9YWh9OtFn+HAxuu0kXEz77W7kcHxz2UcMMHwfUksEdS5nH4/b0/nRwA8B3hxqOYu3F41rhXm4V17L3p4LijS+GGVqGrbWbr7dPsIqauWy0+RcSz7DpGSCcHAMSXrtBPIdx4iBez9fYsu4gp6laLz+FkyutYx96DDo67+xTCjZtIXofxIgxjegidHAA0r5zOoHPjYc6EG0flWeJ65j/eg4DjDsp5xMvsOiq2KVsoOLKS3Bo6+jBPIuJvA5sAaFEJN5wA+DAXEfE6u4gpK88Sm+QyarYsz6TcQsBxi/KFdJJdR+VeZBfQknIkmQ/Zh9nvZRRyANAM4cajdGHuxlA8U9zsRMhxOwHHDYQbd/Jhtt7usotoTWmRdDzZwwg5AGhGt1q8DeHGYzy31h1G+X22vr2ZkOMWAo5rCDfuxNnViWbr7evQyvdQ+5DjJLsQADiWsp59k13HiL22DXtw78JW7NsIOW4g4LiCcOPOXmvXS/c8DB19qFlEnAo5AJgi69lHO5utt7oJBlaeLWzFvp2Q4xqOib3EUbD3cjFbb3/JLgJn2R/Ia4sYAKagrGc/hQH5j+FYzmTdavFneCa7C1+r39HBUQg37k2yWglDRw/ivRQcgLG7tJ5dJpcyZl14YKyBte3dPIl+27UXnYWAIyK61WIewo37+GA/Yl3K0FHzUB7npFstTn1AADBGpaPTW+/HE25UoDxr6K69m33IMc8upAbNBxw+DO7NYNFKzdbbtxFxllzG2J2EFByAkbm0XXWeXMrYvSidsdTBwNG7exIRfzolsPGAowwXNLvgfl5Itav2Ovq9eDzcPgVv/gMCgPqV9eyfYT37WB9KRyyVKM8cL7LrGJH9KYHPsgvJ1OyQ0W61eBUR77PrGJnNbL19ml0ENyvdB3+Hhc5j7ffgCowAqJL17MGczdZbD9KV6lYLc2Xur9kB+s11cHSrxawME/RhcD8S1JEoaffT0NL3WLPoW/1OsgsBgO9Zzx6MYe31exHWtff1vtXZck11cJTBK5/CvI2HaDYFHKvyYO5kkMN4V2acAEAqx8AeVBcRP9t+XT/dSg92ERHPZ+vtLruQoTQTcHSrxTL6D4PmUqwDsDVlpHwYHNRZ9EGfRRAAKcp8qNPwsu4QbEUdGVtVHqyLPuTYZBcyhCa2qHSrxdswTPShbE0ZsdJ1c5Zdx0SchBNWAEhSXtZ9DuHGoTgxZXxsVXmY/fDRt8l1DGLSHRxa+A7C1pQJ6FaLTxHR9ETlA/LGB4BB6cg8uBdOTGDhpiAAABnrSURBVBkn3wuPtom+m2OyQdFkOzjK8Th/h3DjMTbCjcl4EY6PPZR9Cn6SXQgA02eY6ME5DnbEyrPJJruOEVtGxN9TPkp2ch0cpWvjTUS8yq5l5LqI+KWlgTRTV743/oyIeXIpU/Jhtt6avA7AwZXPbVtSDstxsBNQDo74M4wfeKwP0Q/Sn1Q3x6QCjrI38TQ8wB3C89l6e55dBIdVhpOZR3NYm5h4qx8Aw/J5fRSG5k9I6UD4lF3HBOyi37K1Sa7jYCYRcOjaOLjz2Xr7PLsIjsOi6Sh20YcctgEB8ChmDBzFRfTzs7yMmBAz5g5qMt0co5/BUbo2/gzhxqHswqkpk1Yewv0ZH9Y8Iv40lwOAh+pWi5l5G0ch3JiuF9E/u/B4r6Jfyy6zC3ms0XZwlK6N05DaHdov3kK3oTyMn2bXMUFn0Z8+ZCEFwJ2U7srTMG/j0Jx8NnHle+fP7Dom5jz6bSujXMuOsoOjtO79HcKNQ3vtA6AdZYK4To7DO4n+lJV5ch0AjECZJWCY6OEJNxpQ/nwNfD+sZ9GftDLKHRKj6uAoLTPvwwfAMZi70ajSDnuSXccEddGn34b1AnClbrV4H7ZZH4uu5IaYx3E0F9G/BN9kF3JXowg4ypvQ9+GL9lh20X8IjLINiccTchyVo2QB+EZZ234KL+2O5UXpVKURZXzBn+E0zWM5jz7o2GUXcpuqA47yhfoq+hNSOB4JN0KO47qI/pSVXXYhAOQqW1JOw2lmxyLcaJR5HIN4F/3Lu2pfjFcZcFwKNl6Gm/+x+RDgi261+BwRy+w6JqqLPvk+yy4EgOGV9e2bsCXlmKxrG2eI/iC6iPgYlQYd1QUc3WrxNgQbQzmbrbeGTPJFWXwZdHZcZ+GUFYCmOCVlEK9n6+2H7CLIpyt5MF1EfJytt2+zC7msioBDx0aKi9l6+0t2EdRHyDGIXfRbVmwNA5i4chLBm7DGPSYv7fhGt1r8GdayQ9lFxO9RSUdHasAh2EjTRcTPNXwBUichx2De1ZZ6A3AY5bP0NAzJPzbhBj8o339/h2fMIVWxdSUl4CiTo19G3zrki25YzgTnToQcg9lEv2d4l1wHAAdikOhghBtcq2wN+xy+D4fWRb8l+2PG+nbQgKNbLZYR8VvYE5XJ8CXuTMgxGANIASbAINFBCTe4laGj6c4i4vfZersZ6oJHDzjKjf5Z9B0bHpJyaYfn3oQcgzqPPoS0fQxgZMqLvNOImOdW0gThBndWDrF4k1xG6y6i375yfux17tECjtIS9DL6cENbUD4fBDxYCTn+DIu2IXTRhxzn2YUAcDfdavE+dG0MxZqWe3OySjW66F/ofTzWyISDBhy6NarlxBQezT7GwenmAKic418HJ9zgwZysUp2jdHUcJOAog5R+C1Oia3QR/VBRD0k8mpBjcLo5ACp06SRAbe/DEW7wKLZdV+08+lkdj17zPirgKENb3oS29Vo5DpaDK6cgfQofDkPSzQFQCbM2Ugg3OAjHx1ZvF/3cyLOH/gIPCjjc2EfBcbAcjQQ8hZNWABI5ISWNcIOD0pE8CrvoX+5t7vsv3ivgKDf207AVpXbCDY5OyJFmE/0Nf5dcB0Azynbs9+Hl3tCEGxyFkGM07t3FfOeAo3wRfAo39jH4RbjBEIQcabrop0+/zS4EYMq83Esl3OCoyvPtn9l1cKtdRDy/6/PtnQKOMmvj9OE1MaAXWtgZkpAj1UX021Y22YUATE23WuyHiHrDOzzhBoPwnDsqd3rOvTXg8Ic+KsINUgg50n2IfiCTIaQAj1Te6r6PiGVyKa36MFtvX2cXQTs8747Krc+7NwYc/rBHRbhBKiFHOkNIAR7BENEqWM+SwnPvqNx4n7g24PCHPCo+DKhCWRy+j4iT5FJatok+6DCHB+COyrr3fdiOksl6llSef0fl2vvFlQGHgSuj4sOA6nSrxWkIObLZtgJwC9tRqmE9SxWEHKNy5cEaPwQc5Q3s3yHBHgMfBlRLyFGFLvqQ40N2IQA10XFYjS769ex5diGwJ+QYjS4ifv7+Zd5/XfETT0O4MQbCDapWpp+/y66jcbOIeN+tFn93q8UyuxiAGnSrxdvoX+adpBZCFxFPhRvUpjxjOcWnfvtjvL/xTQdHt1o8i4hPQ1XEg0i6GRUpeFU20d8/dsl1AAyurHPfR8Q8uRS+hhvmRVGtcs/w8r9+zy8/G38fcPwdbvo182HAKAk5qmM+B9CM0sH2JszZqMVF9A8ku+xC4DZlTs/nEHLUbDdbb3/e/48vAYcHkOoJNxg1HxDV6SLiY0R8EHQAU9StFvPog42T3Eq45CL69azPHUbDGnYUvoxvuDyD401OLdzB/sNAuMFola/fp9E/WJNvFv19/88ScANMQrdazLrV4n2Ys1Gb8xBuMEKX1rCexer1Jcv4KUL3RuUk3UxKmVz/OSKeZNfCN3YR8dp8H2CsyufLq4h4Gd601uasDB+H0bKGrd6L2Xp7tu/g+C21FK4j6WZyytezFLw+84j41K0Wn524AoxNt1q8ir5j400IN2rzWrjBFFxaw3oZVKffIiJ+KvsT/86thStIupm8brU4De3DtdpEP4h0k1wHwLVKF/KbMCS/Vl/2xcOUWMNW6+f/ChOla/RCuEELytf5u+w6uNIyIj7r6ABq1K0WJ+X0v9MQbtSoi4hfhBtMVVnDel6rz/KnbrX4FBHPsishIvoPgxf2wNMac4BGYRM6OoBkOjZG4SL69aytqExet1o8i34Na2tcHc5/6laL/w1/IDXwYUDTHME1GpsQdAADE2yMhuH4NKesYU/D8NEadD91q8X/y66C2ETEcx8GtM4HxKhsQtABHJlgY1TMj6NZ5YSVT2H8QzoBR753s/X2bXYRUAsfEKOziYjf7bMGDsVxr6P0erbefsguArJ1q8Xb6ENZkgg48pi3ATcwnXp0dtEHtmfJdQAjVU72OwnBxphYz8J3zOXIJeDIcRH9lpRddiFQMyHHKO0i4veI+GDbHXAXJdh4E+73Y7OLfj1rfhx8p9zXPoVt14MTcAzvw2y9fZ1dBNSsfCichm0qY9ZFxHn0XR275FqACpUjqN+Ee/1Y/SLcgOuV7XbvQ3g7KAHHcLTwwS0u7bu2d3FaziPio4GkQITBoRPzLnTswY1sWRmWgGMYtqTALcrN/31Y8E7ZLvrF8LnFMLTFfI1J20U/ZNRLPLiGLSvDEXAMo4uIs+jfYO5yS4G62I7SJPdEaETZhvIyIp4ll8LxbaLvVt4l1wFVcTLUsAQcwzuLflFvzyJNKzf7N9Hf8GnXJvp7ojd/MBHl/n4S/WJ+nloMGT5EP39Jpx5NuzRA+VkINgYj4MizCYt6GlXOCJdic9ku+tNXzrz9g3Eq3Rq/hYF69J16H2fr7dvsQmBoOtdyCTjy7cKedBphsBx3dB4R/56tt2fZhQA3063BLXbRd3OcJdcBR1fWuS/DnI1UAo56OFKRyTJAlAfaz+r43bY+qEu5r/8W3lByN7swiJQJMkC5Pj91q8WfIWWqzSZsX2ECSovemzBAlMe7iK9bWHS7QYJutXgSX7egWMjzEJvoX+ZtkuuARxHyVuvip261eB+G/NVqF/akM0KCDY7sPCL+Hbb2wdGVt5PPwhYUDmsTgg5G5lK3xm/hflirDz+V9OlTdiXc6jz6Nm1dHVSr7D38LQQbDGO/te/f7o1wOGWuxj7U0OXLMW2iX9+eJdcB19KtMSrPf4qI6FaL/w2thmOx35P+UVcHtTA8lAoIO+ARLoUa/wqLeIa3C8NIqUjp1tifhDJPLYa76mbr7f/sAw7bVMbpIiI+hjZtEpTF8KvQpkd9hB1wB0INKrSLfnv2B2tbhqZ7bfQ+zNbb1/uAYx4Rf+fWwyPZwsIgyv3iTfQfADq/qN2XsCMiNhbMtO7STI1/he2E1MvpggymbEH5V/TzNRivn2fr7e6n/f/qVovP4YNuCry55CjKzf9luE8wbpfDjl1yLTCIcvrJfgHvrSRjswmnC3Jgl0INL+ymYTNbb59GRFwOOJYR8TmrIo5C2MGjmBbNxF3E19NYLrKLgUMpbdbL+NqlMU8sBw5lF04X5BEcdT1pT/enMv10+Z92q8VpaM2ZKmEHd2ZaNA3a3yP/CN0djFBZuC/D1hPaYGs2d6JTowlns/X2xf5/fB9wzKKfxeEPf9rsSecHUm34xkX0bdF/WEBTo9Jht4yIX0OXBu3any74u048In7oYBNqTF8X/eyNL8+zP/3wM/rjHk8HLIp8+7eW595atsUWFLizTXzt7tjklkKLLi3a94GGWRrwrV3YwtKkS8OTfw3dx6158f3x0j8EHBG2qjRuFyXw8NZymi61Mf8WFsjwUJsQeHBEZcH+JAQa8BAX0YcdXt5NVNl6sg805rnVkOSbrSl71wUcs+gHjvowZRNft7Jo/RupS9tPfAjAcXzZ0hIRFxbU3Nel8PmfYcsJHNIuvs7ssJYdKXOG+M5F9INFfxi1cGXAEWEeB1fq4tu3lj4kKlW+fy8n276PYVi76D98/4r+vnlh3hF7l7ozLndoAMd3eaD0uftyvS4FGvt7pLUsez/M3bjs2oAj4ssX1ufwBcXV9oHHX6FNO1056nmfauu+gvpclB//CaFHM64IM56EdRXUYn9cuHVssrKOXcbXLjb3Sa7SRd+5ce2L9hsDjogvH8yfwgMTd7OJS28ttWkfR+nQ+D7ZBsZnV378Ef29c6c7brzKAn0e/QJ9H2pYpMN4bMJ8paO7dArU/l65TCyH8biIiOe3PV/eGnBEfHmY+hS++Li/Lvovxj+itGxbvN9f6aZ6El9TbYEjTNtF9PfMv0LwUZVLAfO8/Pj10t8D07Kfr/RXWMM+yKU17Dx0sfFwm+jDjVs7X+8UcOx1q8XbiHjzoJLgW5voF+/7Vu2dbo9eeQP4JCL+EVJt4Fu7+Drf4/+iv3+GN42HdUWI8Y/yVwtzYBNfu5V37r+90pUxj37d+o9Lfw+P9W623r6960++V8AR8eXh6zS8qeA4Lr+13O1/TC38uPQhsF8sewMIHMKm/PWP8teL6DvpJncffYzyRnEWX++7/198vR/rkAPuaxdftxvuu5cnd9+9tH7d//hn9PfNZVJJTNsuIl7cN0S8d8AR8eXNxmn0pzPAUHblRxd9ABLxdTHf1dI2eOnNX8TXBfM+ybZ4BrJtyl8v30v3C/K9UQ1AvbTo3lte+vtfy1/df4EM+6B533m3Kz8iKrrXXgp+I77eQ/fr1/0PGMp59OHGvb8/HhRw7HWrxbPogw7tmtRm893//uOqn1R8v7Dfu6kVef+2b8/CGZiy/QL9sssByTH9esU/s1UEmJrv77M3rV03N/x/yxv+v33HxZ57KbXpog82zh/6Czwq4IjQzQEAAAA8yoO7Ni57dMCx160Wr6IfQCoFBAAAAG7TRT9I9MMhfrGDBRwRujkAAACAOzlI18ZlBw049szmAAAAAK7w6Fkb1/mvQ/+CERGl0J8j4uwYvz4AAAAwOmcR8fMxwo2II3VwXNatFsvouznmx74WAAAAUJ1d9F0bm2Ne5OgBR8SX2Rz7IaQALXod/ba9l2H7HgAA7Xg3W2/fDnGhQQKOvW61mEffzbEc8roAiXYR8Xy23l5EfAl830fESWJNAAyni4iP0Q/T+xS6moF2bKLv2tgNdcFBA449Q0iBRlw7GbpbLZ5EH3Qshy4KgMF8iP7NZRfhxEGgGUcbInqblIAj4ssN/k30W1cApub1Xc7zNqcIYJI2ccNby261eBV9yA0wNd8Eu0NLCzj2bFsBJuYi+kXtxX3+pW61OIl+sauzDWC8LqIPuDe3/cTSyXcaEU+OXRTAADYx8HaUq6QHHHtl28r78BYTGK9HJdaXBjIbRAowLrvo7/9n9/mXdDQDE7CLPtgdfDvKVaoJOPa61eJtWNwD43LQfYYGkQKMRhd9sHHrlsQbfxHz6YDx6SLi41Cno9xVdQFHhMU9MCrXDhJ9rLKF7024FwLUZn8yyodD3f/L+vdT2LYN1O8s+q6NlDkbN6ky4NhzygBQsYO8tbvThdwLAWryLg4YbHyvDCB9E7o5gPpsog827jVrbkhVBxx7ThkAKrOJhCFK5V74JgQdABnO/v/27uY4bhsM4/jTQdyB97B3qwOxg+i8FykVKB14UkHsCkxdcpY7oDqg7znAHdAd5AAgxFIr7QeXxNf/N6OxkvGMcXr5zoMXgGywbZb+h7iEH0BijGzv20Vex1FZBByee2Xgswg6AMSx2tTGu4sg6ACANbVaKdiYYpoDQGRGF1ygHFNWAYfEKwMAoumUwNNXIYIOAFhUq0jBRohpDgARXP2eobVkF3B4BB0AVpLE1MZ7CDoA4KpaJRBsTDHNAWAF2QYbXrYBh8crAwAWtNgLKUsg6ACAWVolGGyE3AbfN0l3sdcCoDitEq+Bp8g+4PAIOgBckZG9Ifo59kIu4YKOR9EAA8Axg2xT/zWnpn7Ybe9kX9faRF4KgPy1KiDY8IoJODyCDgAzfZEt8llMbbyHeggAb8p+DNtNc3yWPbINAOdqVVCw4RUXcHg09gDO1Cnxd70vFdTDO3F2G0DdjGyw0eYabEwNu+2N7DRHE3kpAPLQqsBgwys24PAIOgAcMcgGG23shSyNy5kBVMwos6cOzzXstg+yQQf1HcAhrQoONrziAw6PoAPAAX8p4/HkOVwj/Fmc3wZQtk62oe8ir2MVHFsBcECrCoINr5qAw2NUG4Bsw/tHLYX+Pby8AqBQrSpq6Kdcv/tN1HagVoPsa4DV1cHqAg6PUW2gSkY22OgiryM5hL8ACmAkPanSybxDXIj9TUzrAbXI/gLluaoNODwXdDzIBh2bqIsBsJRq7tmYi5oIIEOdpCdq/Nu4nwMonlFhFyhfqvqAI+SK/6Okm8hLAXAd1afYcwy77Z1sTWwiLwUApvz49dcSX79aAtPLQJF62TrYxl5IKgg4DuBMOlCEL7LnDgk2ZnLHVx5lJztoigHEZMQu5Swu6PhbXLwP5KxTRRcon4OA4x2cSQey1KrCC5XW4ibd7kUADGBdrewxlC7yOorBC4NAdqq9OPQcBBwn4Ew6kIVWFPzVMNUBYAVGTGssjqADSJ4RtfBkBBxn4kw6kJxWBBtRuamO32Wn3QBgDu7WiISgA0hOJ1sLn2MvJCcEHBdi9xKIrhXBRlJcXfQh8CbqYgDkppN94vWZHcq4CDqAqAbZHvcrPe5lCDhmcsdXfEPP6yvA8loRbCRv2G1vZOsidxgBeIuRDTVaanp6CDqAVfWyx1AIeWci4LgiGnpgMf65V5rgDLmjfffiCAsAjqBkh6llYDHUwwUQcCwgmOrgpQFgHiO7u/eFNDt/1EagWr6J/85Z8ny5Gv6nbA3fxF0NkLVOHMlbDAHHwoLU+058DIBT9bJpdht7IVhGcF/HvTjeB5TKhxpt7IXgutzl0hzPBk5nNE5rmLhLKRsBx4rcmLZ/aYARP+C1VtLTh3/+7SKvAysi7ACK8izpu9iZrMKw2zaytfsh7kqAJDG9FgEBRwTBmDbPKgJcMocAYQeQnUF23JpQo2Kudj/ITnWwiYfaEfRGRMARGc08KtbJTmu0kdeBRBEGA8liVxJvcsdXuGsJtek13qthIq+lagQcCSHsQAWMOH+IC7iwoxHH/IBYjGz9fuK2f5yCe+hQAUKNBBFwJIqwA4XxTTE7fbgK9yy33yGkRgLL6DSOWZu4S0HOeC4cBSHUSBwBRwYIO5Ap/wFoOX+IJbka2chOdzRiugO4lNEYanTUblybm8Z7ED0t8kKokRECjsxMGnlScKTGiAtDEZm71b+RrZM00MD7niW9iMYdKwsuJr0XR1iQHn9RaEdtzAsBR8YmZ9Ib8XFAHEacy0aigjp5K86BA5Kd0HiRbdq7uEsBrODYIXUasRgxwVYEAo6CuI+Df3GAXUssqdf4CgqhBrIRTMHdimAYdfD12ocaNO1IGmEHVtRrvGeIfrYQBByFYroDC+jEZXMoTBB4fBIXlqIMnVyYIakn0EDOuIcOV2bElEbxCDgqwSV8uMCg/bPZfARQvCAcvtE45QGkatAYaPQcOUHJXH2+03jkkF4Wx/gayV0aFSHgqJQb/2s0NvB8JCCNjTKjeoAT1MtPssEHu4iIpZMdqf4hmnVULjiaTRgNLwx9O3rZOhFwQBLn0ivG2WzgTG4XcRp6bCIuCWXq3Y+fzqBRB94wuVC6EUF0LYz2+1gTczFIAwEHDjowpn0jpjxK0GlsmAk0gCsJQo8bMemB8wwaw4wfIswAZpsEHj6QRt58rXxxf9LH4iACDpzMjQKGzXsTdUE4xmh/96+LuhqgQq5ubjSGxRsx7VGzTrY2//S/s+MIrGPYbRvtb9xtYq4HR3Ui+MUFCDgwizva4oMPmvd4jIJz2eLmfCBprtH2Ux+f3O9NxCXhevwuoxFBBpAsjhsmw7gfP5nRUy8xBwEHFuGa9437Ifi4nnCU+afGDwFhBlAAFxpvNB4LJPxI0zTE6CUNTMoBeZscN/wY/M4x7fmMxiDDyAa/XbzloFQEHFiVG9f2zfpvGj8anFXf18s20C8KQg2CDKBuLjyWxsDDByAbESJfk6/BRjbA8HV4YEwaqM8k+Pggu3lH//qar529pF+yE2zUTayKgAPJCD4e4Qfj1v25UTnNuy/8kg0wpOCDQIgB4FJBHZX2dx1vg7/WrLmmhIS118gGF9JYf8VuIoBzHelfS5r+MO5Hon9Fwgg4kJ1gCkR6HXzcTv76kul62Cx7RmPTLNnk2i6ExhlAYib1VHodfnzU4XB57ab9/xDiwP//Ffy30diASzTdABIRTOBJ+7X2UJ1dssYeqqcvwe9GYx1l+gLZ+Q+wWxlZ+wpOJAAAAABJRU5ErkJggg==" - }, - "asset-7e4f7119-b2d8-4527-9bd8-887cb25974e7": { - "id": "asset-7e4f7119-b2d8-4527-9bd8-887cb25974e7", - "@created": "2018-09-06T19:44:52.474Z", - "type": "dataurl", - "value": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABDgAAAQ4CAYAAADsEGyPAAAACXBIWXMAAAsSAAALEgHS3X78AAAgAElEQVR4nOzd23Wb17U24Ln3+O+jDgJXEPliXQeqIFQFpiqwVIGkCihXQLgCMRUIvl4XZiow0gF2B//Ft2hRMg8gCGAdvucZw8OKh2PPxCYO7zcPEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAME//U7sAAGA8OecXEfHykT9tm1K6PkU9AMD4BBwAMDN3hA93hRH/KH/8e8sjlfWYTfntrj/+3+/+2PrWr4UoADATAg4AGEDOeVl+uSi/RXwbUtz+43N2HRHbW7/+v/Lrdfm9QAQAOiXgAICGfddtsSy/vwkuFiG0OKZ1+f0mpi6RbZSARAgCAO0RcABAZTnnlzEFFsuI+FtMgcYuOyyo68/AIyL+E1/HaDYppU21qgBgpgQcAHACOedFTN0Wy/gaYtz8McZ0E378Fl/Dj+uU0vaB/w4AsCcBBwAcUOnGWMQUYPzj1q/hxk3nx3VMoy/XIfgAgGcTcADAHm7txlhGxN/LrwUZPMdN8HHT8XFt1wcA7E7AAQCPKOMlNwHGP8NoCad10+3xn5hCj3XdcgCgTQIOALildGYs42uYcbPwE1oi9ACA7wg4AJi1nPMyvg0zFjXrgWdYx9fQY+2SCwBzI+AAYDZudWfchBnLmvXAkW3i606PtX0eAIxOwAHAsMrujGV8DTQsAWXu1vE18FjXLQUADkvAAcAwvgs0lmHcBB6zDoEHAIMQcADQre9GTs5CoAHPtY4p8Lgy0gJAbwQcAHSlLAVdRsS/wsgJHNM2psDj32FpKQAdEHAA0LRbYyf/Kr93shXq2ETEVUT8llK6qlwLAPyFgAOA5pQujZtAQ5cGtOkqvo6zbCrXAgACDgDqK7s0zuLrLg1dGtCX65jGWX61uwOAWgQcAFRRRk9uhxrAGLYxdXf82ygLAKck4ADgZG6FGj+F0ROYg9uLSq9SStu65QAwMgEHAEeVc34ZU6DhjCtwFcIOAI5EwAHAwQk1gB0IOwA4KAEHAAdxa/zk5xBqAE8j7ADg2QQcAOzNTg3gwCwoBWBvAg4AnuTWSdd/hesnwPHchB2/OD0LwC4EHADsJOd8O9R4UbkcYF42EfFrRKxSSpu6pQDQKgEHAPcqIyg/h2WhQDvWMYUd9nUA8A0BBwDfuDWC8lNELOtWA3AvIywAfEPAAUBE/Hna9aZbwwgK0JNNRPwS0wiLrg6AmRJwAMzYrW6Nn8MVFGAMq4j4NaW0rlwHACcm4ACYId0awAxsQlcHwKwIOABmJOd8Hro1gHmxqwNgJgQcAIMrl1DOYwo2dGsAc7aOaXxlVbkOAI5AwAEwqJzzMr6OoQDw1Tam8ZVPxlcAxiHgABiMMRSAJ1mF8RWAIQg4AAZQrqG8DWMoAPtaxxR0XNUuBID9CDgAOlb2a7yPaccGAM+3iYiPEXFlfAWgLwIOgA7ZrwFwdPZ0AHRGwAHQkZzzWUzBxrJyKYzpOqYvdTc2EfHfHf68B/+ad305zDm/jN3HqRblt+/987v//JS/Juzq5szsx5TSpnItADxAwAHQgbI49H3c/SUP7rKNKYiI+DaouB1ObEb+wlY6nW7cDj/+UX79Iizj5WlWIegAaJaAA6Bhgg3usSm/bSPiP/FtmHFnxwQPu9VRcjv0uOkQWdaoiaatYwo61pXrAOAWAQdAY1xEoVjH1wBjc/ObJ8f13OoIWUbE3+JrV4gukPlah6ADoBkCDoBGCDZm6WZc5Lf4GmLowOhQuWi0iG/DDztB5mMdgg6A6gQcAJUJNmbhOqbw4j83v04pXT/432AI5ef7Juz4ewg+RrcOQQdANQIOgEoEG8NaxxRi/CemIGNdtRqa9F3Hxz/Kr426jGMdgg6AkxNwAJyYYGMo65jGS3RlcBBlz8fLmEKPm24P+rUOQQfAyQg4AE4o5/w2pqsogo3+XMf0ZeU/Me3JEGZwEiX0WMbX0GNRsRz2sw5BB8DRCTgATsC51+5s42uYsfalhJaU8ZaXMZ2xXYYuj55cRcQ715AAjkPAAXBEOeeziLgIwUbrNvFtoKE7g67c6vK4CT1o2yqmjo5N5ToAhiLgADiC8mXjffii0apNfN2fsfYlg9EIPLrxKaagw2logAMQcAAcUGkdvwxfKFpzM3Ly7xBoMEO3Ao9/hZGW1mwj4peI+CToAHgeAQfAAZRg431EnNethFvW8TXQMHICRbnktIwp7FiGEbpWbGLq5lhVrgOgWwIOgGdw8rUpm/g21PAkFHZQAtqzmMZZzupWQ0wXm95ZbgzwdAIOgD25jNKEdejSgIMqy5F1d9S3jog3RuoAdifgAHiiMst+EebYa7i9S+NKlwYcV875ZUxdHXZ31GMRKcCOBBwAOypt3BehhfvUthFxFRH/Tild1S4G5sooS1XbmEKOT7ULAWiZgAPgEfZsVLGJKdT41egJtKe8Lt50dgg7Tsd+DoAHCDgAHlBm0S/CHPopbEKoAd0RdlRxFVPQsaldCEBLBBwAdyit2JcxLdnjeDYh1IBhCDtOahsRv6SUPtQuBKAVAg6AW26No7yvXcvA7NSAGbgVdvwcFpQe0yamayvrynUAVCfgACiMoxzdTaeGUANm5taC0p/Da+yxGFsBZk/AAcyecZSjuo6IX8JJV6Aop2d/jinwsLj5sFxbAWZNwAHMWs75Q7iOcmjbiFjFNBu+qVsK0LLSOfdT2NdxaK6tALMk4ABmKee8jGkcxVz44RhBAfZihOVoPsXU0aGDDpgFAQcwK2Xp3fuYFonyfJuI+DUiVro1gEMoAfRPEXFet5JhbGNaQip8BoYn4ABmwxLRg7qKaQRlXbsQYEwlkD4PXR2HchVT0KGbAxiWgAMYXvmQfBlmvJ9rE7o1gAp0dRyMJaTA0AQcwNBK18ZlWCL6HOuYdmusKtcBzFzZ1XEelkM/1zqmbo5N5ToADkrAAQzJ6ddn28bUzvzRB2CgRTnn85i6OpZ1K+mWbg5gOAIOYDg557cxLRL1dO/pNjGNoXwypw30wPjKs61DNwcwCAEHMAxdG8+yDmMoQMeMrzyLbg5gCAIOYAh2bezNNRRgKGWx9FlMnXyLutV0Zx26OYCOCTiArrmQsrdV2K8BDK7s6fg5Il5WLqUnujmAbgk4gG7p2niybUT8EvZrADNT9nS8DyOMT7GOiNfeL4CeCDiA7pSujYuwUG5Xgg2AsJB0D9uYRlauahcCsAsBB9CV8uH0MsxV72ITU5vxqnIdAE0pC0nfh6BjV6uIeCckB1on4AC6kXP+ENMHUh62CcEGwKMEHU+yiWlk5bp2IQD3EXAAzSsfQD+HJXGP2YRgA+DJBB1P8jGl9KF2EQB3EXAATSsb8C/CItGHbEKwAfBsgo6drcM5WaBBAg6gSRaJ7mQTgg2AgxN07MQCUqA5Ag6gOTnnlzGNpCwql9KqbUzL3la1CwEYmaBjJ59iCtstIAWqE3AATck5v42pc4O/cu4VoIISdFxGxLJuJc26jqmbwwJSoCoBB9CEMpJyGRFntWtpkGADoAHlVPn7EHTcRXchUJ2AA6jOSMqDVjG1/m4q1wFAUYKOy/C+dZdVTEGHQB44OQEHUJWRlHutw4Z6gKaVS1/vQ9DxPSMrQBUCDqAKIyn3WsfUsbGuXAcAOyjvZ28j4udw0vw2IyvAyQk4gJMrIymXEfGydi0N2YSTrwDdct78Xp9SSu9qFwHMg4ADOKnSznsRnnLdsEAUYCAlxL8Ii0hvu46I18YugWMTcAAnk3O+iKmNl8kqLBAFGFLO+SymoGNRuZRWbGMKOda1CwHGJeAAjq607X4JIyk3rmOaS17XLgSA48o5fwj7OW57l1L6VLsIYEwCDuCoSqvul/DBLsLCNYBZyjkvYrq2cl63kmaswilZ4AgEHMDRlH0bl7XraMSnmMZRfJgDmKmc8zKmsRUdjfZyAEcg4ACOIud8GZ5URUxnX9+llK5rFwJAG3LOb2Pq6Jh7d6O9HMBBCTiAg7Jv40/GUQC4VxlbuYiIs8qltMBeDuAgBBzAwZR9G5/DxvhVmC0GYAdlbOUyvHeuUkpvahcB9E3AARxE2bdxEfNut91ExButtgA8Rel+vBlbmbPriHjlAQGwLwEH8GzlBN7cP5R9TCl9qF0EAP0qnZAXEbGsXEpN25hCDrurgCcTcAB7K0+cLmLey0TXMXVtbCrXAcAgLCGNbUzvrVe1CwH6IuAA9mKZaGxj6tqwFA2Ag7OENCIsHwWeSMABPJlloro2ADiNnPNZTEtI59rNYfkosDMBB/AkZdv755jnBy0tswCcXOmavIz5dnOsI+K15aPAYwQcwM7KpZTL2nVUchVTuOHDFQBVzLyb4zqmkGNTuxCgXQIOYCc554uYTtjNja4NAJox824OF1aABwk4gEflnC9jnpdS1mHXBgANmnE3hwcPwL0EHMC9ZnwpxYUUAJo3826ONymlVe0igLYIOIA7zTjcWIeuDQA6knN+GxHvY37dHJ9SSu9qFwG0Q8AB/EU5A/sl5vdB6WNK6UPtIgDgqXLOi5iunM3twYQzssCfBBzAN2Yabmxi2sxuaRkAXcs5f4ipm2NOXDoDIkLAAdxSzsBexLzCjVVEvPOhCIBRlIcVnyNiUbmUU7qO6cKK93OYMQEHEBF/hhuXtes4IVvYARhW2aV1EfO6gibkgJkTcABzDDfWYZEoADMww+7MTRg7hdkScMDM5ZwvY15PdywSBWBWZriAdBtTJ4eQA2ZGwAEzNrNwYxvTE5117UIAoIac80VEvK1dx4kIOWCG/rd2AUAdMws31hHxg3ADgDlLKb2LiNcxffkf3YuI+JJzPqtdCHA6OjhghmYWbhhJAYBbZjiy8ialtKpdBHB8Ag6YkbJR/UvM4wONkRQAeMDMRlaEHDADAg6YiZmFG+uYwo05tOACwN5mdmVFyAGDE3DADMws3PhUZowBgB3knF/GdC5+Dp8ThBwwMAEHDG5G4cY2pg8tV7ULAYDelM8LlxExh6WcQg4YlIADBjajcOM6pg8rTsEBwDPknN/GNLIyOiEHDEjAAYOaUbhxFdOHFPs2AOAAcs7LmK6sjL6XQ8gBgxFwwIBmFG44AQsARzCjU7LvUkqfahcBHIaAAwYzk3DDvg0AOLLymeIiIs4rl3JsQg4YhIADBjKTcMO+DQA4oZns5TCuAgMQcMAgZhJurCPitX0bAHBaOeezmK6sjLyXQ8gBnRNwwABmEm6sUkpvahcBAHOVc34Z016OReVSjknIAR0TcEDnZhJu+LABAA3wuQNomYADOjaDDxnbmEZS1rULAQC+yjlfxtjLR4Uc0KH/rV0AsJ8ZhBubiHgl3ACA9pSx0Xe16ziiy5zzee0igKcRcEC/Rr5Nfx0RP7qUAgDtKqdV38TUcTkiIQd0RsABHSptocvadRzJKqbOjVE/LAHAMMoYx6sYO+Q4q10EsBs7OKAzg8+8fkopjdzuCgBDKhdWLmPM7tJtTA9fdJZC4wQc0JHBww3LvACgY4PvBxNyQAeMqEAncs4fYsxwYxvCDQDoXhkvfRXTuOloXkTEl9KpAjRKBwd0oCy4uqxdxxF4GgIAAxq463QbET/YFQZt0sEBjRs43NiEcAMAhjTwGdmbTo4XtQsB/koHBzQs57yMaZZ1NNfhUgoADG/gBzU+y0CDdHBAo8qM5+fadRyBDwQAMBNlx9abGO+M7Kif06BrAg5oUAk3vsTUBjmSqxBuAMCslJDjVYwXcizLrhGgEUZUoDFlpvP3iFhULuXQVmUeFwCYoYEf4HxKKY24bwS6o4MDGnLrfvyicimHJtwAgJkri8VH7OR4W3aNAJUJOKAtn2Oa6RyJcAMAiIg/Q44fYtrJNZJLIQfUZ0QFGjHovfg3Ze4WAOBPt7pWR3qws41p19ho4Q10QwcHNCDn/CGEGwDATJSF469irE6OFxHxpewaASrQwQGVDXofXrgBADxq0E6O63A1DqrQwQEVlYRfuAEAzNKgnRw312KAExNwQCW3TqWNRLgBADzJqCFH2a8GnJCAAyoo7ZiXMdYdeOEGALCXQUOO87JnDTgRAQfUMdo5WOEGAPAst0KOq9q1HNB752PhdCwZhRMb8ByscAMAOKjBPi85HwsnooMDTijn/DbGebOOEG4AAMfxLsYZV7k5HzvSaDI0SQcHnEjOeRljLRUVbgAARzPgCVnnY+HIdHDACZSLKZ9r13FAwg0A4KgGXDz6MiIuahcBIxNwwJENeDFFuAEAnMSAIYfLKnBEAg44vpEupgg3AICTGjDkeJ9zPqtdBIxIwAFHlHO+iIhl7ToORLgBAFQxYMhxWUaYgQMScMCRlJvnb2vXcSAfhRsAQE2DhRwvYgo5RhlhhiYIOOAISiI/yhKpVUrpQ+0iAABuhRwjXCJ5GdOeNuBABBxwYCWJ/xxjLBVdpZTe1C4CAODGYCHHmaWjcDgCDji8y4hY1C7iAIQbAECTUkrXMU7IYekoHMj/1C4ARlIS+Pe16ziAdUrpVe0iAAAeUsaCv0T/nbPbiHhVghtgTzo44EBK8j5CuHEdEa9rFwEA8JgSCLyrXccBWDoKByDggAPIOS9ijCVR1zE9PRih3RMAmIFy6W2EsdqRltRDFQIOOIwRlopuI+K1cAMA6E0JOUbo5DjPOZ/XLgJ6ZQcHPFPO+TIizmvX8UzmPgGYhbKzYRkRf4/pifn3NhHx35j2Ua1PVhgHMcjnsoiIH30ug6cTcMAzlIR9hNEUb6IADKuMkp5HxE/x9Etn64j4LQQe3Rgk5NjE9PlMZy08gYAD9jTQ1u43pa0TAIZSFja+j4i3B/pLbuNr4HGVUtoc6K/LgeWcf4+7O3R6cpVSsvgdnkDAAXsoH5i+RP9vnO9SSp9qFwEAh1aum13GcR9EXMcUePyqE7ItPqvBPAk4YA+DtD6uUkojbBwHgG/knC/icF0bu9pGxFVE/DuldHXivzd3KKNJv0f/3bZGiWFHAg54okH2bmh5BGBIjTyEuBll+XdM77n2KFQyyEjxJuzjgJ0IOOAJBnkScB3TxRRvkgAMpZFw4y5XIeyoxsMpmA8BBzzBAAurthHxgw9XAIwm5/whpoWiLTPGUknO+W1EXNSu45kshodHCDhgR5XmeQ9pG1PnhhlOAIaSc17GNIbQk5uw4xfvzafRcIfPrnyWg0cIOGAHnX5w+p7UH4Ah5Zz/iIhF7TqeYRMRv8a0AHxTt5Sx5Zy/RMSydh3PcJ1S+rF2EdAqAQc8opwZ+yP63rvhxBgAQxpk9OC2dUxhh30dRzDI+dhPKaV3tYuAFgk44BE5588RcVa7jmdwDhaAIQ3yEOI+RliOZJDLKq9SSuvaRUBrBBzwgAGeCmljBGBYnSwWPYTriPgldHUczADjxxbHwx3+t3YB0KqS7vf8oWkbEa9qFwEAx1C6N36uXceJvIzpzOkfOefL8hmFZyjdDz2PebyI/k/fwsEJOOB+l9Fv6+LNlm2pPgCjehv9vk/v60VMV0B+zzn/nnM+r1tO38p+slXtOp7hrHQbA4URFbjDAC2vLqYAMKzBd2881Tam8RUXWPaUc/49+l06uo2IH/2zh4kODvjOAKMpn4QbAAxujt0b93kR0+eWm/GVZeV6evQqpqCgR0ZV4BYdHHBLeSL0e0QsKpeyr3VKyd4NAIale2Mn1zFdX1nVLqQX5QHX77XreIaPKaUPtYuA2nRwwLfeR7/hxiYiXtcuAgCOTPfG415GxGXO+Y+c89sSCvGAcor3Te06nuG95bOggwP+NMC5sB/LmzMADCnnvIjpKbsv7E9jT8eOcs6XMS1y7dF1SunH2kVATTo4IP5sd+15fvGNcAOAGXgfwo19fL+nY1G5npa9i2nEp0cvy6J8mC0dHBAROeeLmFpee7RKKfXcUgkAjypfyv+oXcdAVhHxa0ppXbmO5gzQKaSrl9nSwcHsldGUXsON65ieNADA6Hq+cNai84j4knP+4vLKt8oYT88Pj3ruSoZn0cHBrHV+NcXdcwBmQffGSaxjusSxrlxHMzrv8HVVhVnSwcHc9Xw15Y1wA4CZ8ET6+Jaho+MbKaV3MQU/PXJVhVkScDBbnY+mfEopXdUuAgCOrbxfLyuXMSfLEHTc9jqmrtkeCQaZHQEHs9T51ZTr8kQBAObA7o06liHoiJTSNqaQo0euqjA7Ag7mqtfRlJ7fZAHgSXRvNGEZMw86yl6Sj7Xr2NN7Z4GZEwEHs1PmEXsdTbF3A4A56bXbckTLmIKOz3P8wlwWdq4rl7EvP0fMhoCDOer1Rd7eDQBmI+d8Hn12W47uLCL+yDlfzjDoeBN97uNY5px7fbgHTyLgYFbKHGKPG6Wvo9/WSADYh90bbTuPiN9zzh/KbrPhlS7aN7Xr2NP7ufxzYt4EHMxGecrwc+069rCNaTSlxycGAPBk5YHEonIZPO5FTEHUH3PpECjdtJ9q17GHnhfsw84EHMzJZUwv7r35mFK6rl0EAJxCecrc4wOJOXsRERc55z9yzme1izmBjzF11/bmbCb/fJgxAQezUF7Ml7Xr2MNVSqnHpwQAsK+30ecDCaaum8+jX1wpXbW9jqpcGFVhZAIOhldexHtsyev5zRMAnqyMk9q90b9lTBdXLkf9Ml26a9/VrmMPi+j3miA8SsDBHLyPPp8E2bsBwNwIN8ZyHtN+jg+V6ziK0mW7rl3HHt7nnHtcug+PEnAwtPLi3WNK7SQsALNS3rPPa9fBwb2I6Qv1H4OOrbyOPk/HXtQuAI5BwMHoenzx3oSTsADMT4/v2exuEdPYypcyijSEjvdxLHPO57WLgEP7n9oFwLGUc2U9flj60dUUAOakPNn/UrsOTupjTB2rPXY//EXO+XNE9HahZBsRP4zyzwAidHAwqLLQqsc5XidhAZijHpeB8zzvI+L3gcZW3sTUhduTXj8vw70EHIyqx8Wi1ymlD7WLAIBTKh2Xi9p1UMUiprGVz72PrXQ8qvLWwlFGIuBgOB0vFu3xTREA9tZxxyWHdRZTN0ePn9/+lFJaR8Sn2nXsoceRbriTgIMR9fgibTQFgDl6G311XL6KiHcR4T378F5ExEXO+ffOOwo+Rn+jKhaOMgxLRhlKeXHubY73OqX0Y+0iAOCUykjCH7XreIJVSunPbstS/1lE/BQRPX8hb1W3S0g7XZq7iWnRfXf/f8NtOjgYRsdtrkZTAJijnjoutzF1bvwppbRJKX0qDyl+iGk0YVOhtlF1u4S001GVRfQ54g3f0MHBMHLOH6K/gOOjxaIAzE2HT7h3fr8u/9t+iqm7o6fxm5Z9iumfQTfdBeXB2+/R3wLdH1JKm9pFwL4EHAyhwzbXCKMpAMxUzvn36GesYxvTl74nfbkuX3BvRliWR6hrbjYR8aZ0R3ShwyAvIuIqpfS6dhGwLwEHQ8g5f47pQ0RPfrRYFIC56XBf1ruU0rPGDcqDmJ8j4jx0dTxXV90cOefLmP659+RVT0ES3CbgoHudpuNGUwCYndLV8Ef08yV/k1L64ZB/wRLw6Op4nk100s3R4b/zEbqM6Zglo4ygt70bm+hv8RQAHEJvZ2E/HvovmFJapZRexdfFpF10IjRmERFfcs4XJUBoVuk06W2h/EtnY+mVDg661mGba4S2PwBmqMN9WSd5in1rV8f76G8hZQuuY+rmaHrst8Nx6k04G0uHdHDQu966N1bCDQBmqqezsBHfnYU9lpTStnR1/BARryLi6hR/34G8jOmc7IfahTziXfTVrbMIZ2PpkA4OutXhWdi9trADQO863Je1LmMkVVhKurfriHjd6pnTnPPb6Cvo89mV7ujgoEulnfPn2nU80TtvEADMVG/jpCfp3rhPSmmTUnoX056OdzGNC/C4m26O89qF3KVc42l6lOY7L6KvQAZ0cNCnDrs3qj4JAoBaOnxqvUopNbcUsnxpt6djd1cx7eZo6uFSzvllRPxeu44n+qHVrhj4ng4OulPaNnsKNyL6254NAM9WOi57es/exhEupxzCd3s61pXL6cFZTN0cL2sXcltZhtrbNb2efoaZOQEHPertRfaj1BuAmbqIvnZI/NL6e3ZK6aYrVNDxuEVMIUdryzI/Rl8LR8/LHh1onhEVutLhiblNOLEFwAx12Irf5ULF8v/zzUJS7tfUyEoZOeppN41xa7qgg4Pe9PRGEGGxKADz1dPejYhO37NTStdlZ8gPEbGqXE7LziLij1ZGVlJKq+irA2epi4MeCDjoRnlRXVYu4ynWKSW37AGYnfJ0elm5jKfYlC+c3SqXVwQdD3sRbY2sVL3Ws4fexsSZIQEHPentRdViUQBmpywW7a17Y5j3bEHHTi5yzp/Lv6vVdLhwdNnqCV64IeCgCx12b1gsCsBcvY++FouuU0rr2kUc2q2g48foaxTiVFq5stLbwtHeHjgyMwIOetHTi+km+krjAeAgypfFVtr/dzVM98Zdyo4OV1futoiILzW7Esrel55GVRa6OGiZgIPmddq90VMSDwCH0ttoymouHZffnZfdVC6nJS8i4jLnXO3f3bL/5brW338PPT14ZGYEHPSgpxfRde9LygBgHx0uFu3tyflBlKDjh5g6VzaVy2nJ25zz7xX3cvT076IuDpol4KBpHX5Y+li7AAA4tU4Xi/4y547L8kDmx+hvB8QxvYxKp2TLHpjVqf++z9DTA0hmRMBB63p68VyNuKQMAHZwEX0tFt2klD7ULqK2lNK2/P/g4spXL6LeXo6ewiZdHDRJwEGzyovmonIZu9qG7g0AZqjsyjqvXMZT9TQOcHQl6Lg5LbuuXE4LquzlKPtgfjnl3/OZenoQyUwIOGhZTy+av8xlSRkAfKe30ZR1SumqdhEtKqdlLSL96m3O+fOJ93J8Cl0csDcBB00qT4MWlcvY1TachQVghnLOb2PaW9AT3R8qmQ8AACAASURBVBuPuLWItKeRiWM5i2lkZXGKv1mHZ2N7eiDJDAg4aFVPL5bv5rykDIB5Kl/4enq/joj4lFLq6RxnVWU/x49hP8fLiPj9VMtHywLYzSn+Xgegi4OmCDhoTuneWFYuY1cbZ2EBmKneFoval7WHMrbyJoytnHr56JsT/X0Oobegk4EJOGhRTy+SPb35AMBB5JzPYmrd78lHHZf7M7YSEV+Xj7499t+oXOZbH/vvcyC6OGiGgIOmdNa9sXYWFoC5KQsXe1ssep1Ssi/rAG6Nrcx5UetFzvnyBH+fnjqOenpAycAEHLSmpxfHnt50AOBQ3kc/i8Bv9LS0sXllbOV1RLyO+Y6tnB/7wkp5kLY61l//wBblQSVUJeCgGZ11b1zp3gBgbsqSxaO35x+Y9+wjKed2f4z5XpO7ubByzF00PT1Q6+lBJYMScNCSn2oX8ASeBAEwR6doyz+k3k5udieltE0pvYv5LiF9GVPIcZQLKymlTfTTxbHUxUFtAg6aUE7NnVcuY1er8mYDALNRFiue5EzmAf3iPfs0vltCOjdHDTmir/9PdXFQlYCDVvT0YtjTmwwAPFt5ENHTe3XEdMr9Q+0i5ubWEtLryqWc2s0Z2YOHHCWk6+Xz57K8XkAVAg6qK3OL57Xr2JHuDQDm6DKmL3A9ccq9kpTSdUrpx+jnS/mhvIiI3490MvVT9HOet7cwlIEIOGhBT8vK5vZGDcDM5ZzPop8l4DcsFm3AjLs5Lg8dcqSUthHxyyH/mkd0rouDWgQcVFW6N36uXceOPureAGBOyvu0xaLs7VY3x9wurRw85Ii+ujh6+XzPYAQc1HYWfbS8bmN+b8wA0ONoisWiDZrppZXLspz3IDrs4ujttYMBCDiorZcZvV/KmwoAzEI593hWu44nsli0YWVs6MeIuKpcyild5JwP2QXVSxdHTzv2GIiAg2rKTO+idh070L0BwKx0OpoSYbFo81JK25TS64h4HX18UT+E80OFHJ11cRhT4eQEHNTUy4ue7g0A5uZ99PEQ4jaLRTuSUrqKqZtjXbmUUzlYyBH9dHEsjnRRBu4l4KCKciN8WbuOHejeAGBWymhKTxfOIiwW7VJKaZNSehXzuVJ3kJCjsy6On2oXwLwIOKill+6NK90bAMxFx6MpFot2rOxNeRV9dCU816E6OXrp4liWB5twEgIOTq58eDqvXceO5vJEAQAi+hxNubZYtH9lvOiHmMfIyrNDjs66OHp5sMkABBzU0Evb68rTIADmotPRlAijKcMoC0jnMrJyiE6OXro4nIzlZAQc1NDLLN4c3lwB4MZF7QL2sLJYdDwzGll5VshRujh6ObnbY3hKhwQcnFTZpLyoXMYudG8AMBs55w8R0ducvMWiA7s1snJduZRje24nRy8P5Hp5wEnnBBycWi8vbr28WQDAs5QFgO9r17GHdxaBj62MrPwY41+02zvkKA/kVget5jicjOUkBBycTM55EX2chl3r3gBgRnq8mrJOKa1qF8FppJTeRcSbGHtk5TmdHL08mOvlQScdE3BwSr08HerlTQIAniXnfBH9jaZETF92mZESaL2KiE3dSo7qfJ8uh/JgroddHMvywBOORsDBSZTNyWe169jB2rIyAOag46spH3VazlNK6ToifoyxT8le7jnK0cvJ2F4eeNIpAQenchYRPZyH+rV2AQBwbOXBQ4+jKZtyYYOZunVKduS9HE8OOcoDuvUxijmwMydjOSYBB6fyc+0CdrAxzwvATLyPPq6afc9oChHxzV6OUV3mnJ/a/dxDF0cvXd10SsDB0ZXt7D3M99q9AcDwOh5N+WSMlNvKg6kfY9zlo5flc/ROUkpX0ceOkh4efNIpAQen0MOL2Db6WM4EAHvreDRlGx5EcIdbezmua9dyBC8i4stTQo7o4+fk5RP/N8HOBBwcVUfLRX9JKY2a/gPAjYvodDTF+zT3KUtnX0UfOyie6ibk2HVvxVX00dHSwwNQOiTg4Nh6WS66ql0AABxTmec/r13HHq5K6z3c69by0VXtWo5g55CjBIE97OLo4QEoHRJwcGw9pLMr5+YAGFnnoynvahdBP1JKb2LMf2deRsSXHf/c1RHrOJQXe57DhQcJODiajpaLOg0LwOguo4+Oyu999BCCp0opfYoxL6y8zDk/GlSWn5nV0at5vp9qF8B4BBwcUw8vWtc2sgMwspzz2+izHXxdvqjCkw18YeU853yxw5/XwwO8Zc55UbsIxiLg4JjOaxewgx5mFAFgL+XLw/vadexhG2M+geeEyoWVVzFeyPH2sfGO8gCvh8sy57ULYCwCDo6iLDJrvRV2W9J9ABhVr6MpvxhN4RBKyPFD9PFl/ykuc87LR/6cHh7k9dDxTUcEHBxLDy9WPbzoA8Becs4fImJZuYx9XKeUPtQugnGUyyKvYryQ43PZeXefHk7GLnYIamBnAg4Ormxq72HWd1W7AAA4hvKlp8fRlAijKRzBrZBjXbmUQ3oRU8hxZ5dW+d+8OmlF++nhwSidEHBwDOe1C9jBldZXAEZUvux8rl3Hnj6WkQI4uJTSNqX0Kvr40r+rRTx8PraHjuWz+0IaeCoBB8fQQwrbw2ZpANjHRUxfenpjNIWTSCm9ibFCjnvPx5YHeuuTVvN0vXR/0wEBBwdVtrU/NAvYgk1K6ap2EQBwaGXJ93ntOvZkNIWTGTDkOC8noe/Sw4O9f9UugDEIODi0n2sXsIMeXuQB4EnKQ4Y7n+J2wGgKJzdgyHFx18LOcjVwc+pinuisvIbBswg4OLQe2stWtQsAgCPo9SSs0RSqKSHHp9p1HNDne4KCHrqXe/geQeMEHBxM2di+qF3HIywXBWA4HZ+EjTCaQmUppXcxzr+H911W6WHZaA97/GicgIND6uFFyXgKAEPp/CSs0RSaUMY4Rgk5Xsa0bPhPnSwbfWlMhecScHBI57ULeITlogAMpfOTsEZTaMpgIcddS0d7eNB3XrsA+ibg4CDK1vbW5357eFEHgKe4jPbHQ+8zyhdJBjJYyHFROrwi4s//bdt65eykh45wGibg4FB6OO20ql0AABxKzvk8+l3KZzSFZpUg4GPtOg7ky3f7OFrvZl7cDmXgqQQcHErrH7DWlosCMIryBeDi0T+xTUZTaF75d3RVuYxD+H6MzbJRhibg4NmMpwDA6ZSnsb2ehI0Yp/2fwZUTsqvadRzAslxaitI51Xr3VOsPTmmYgINDaH08ZRvtt+MBwK4uYrqS0KN3RlPoyUAhx/uc87L8uvUHf8ZU2JuAg0NoPWW9Sim1vlAJAB5V9m6cVy5jX+uU0qfaRcBTDRRyfC4dYD08+DOmwl4EHDxLJ+Mp/65dAAA8V855Ef3u3diG0RQ6VkKOHoKBh7yIiM9lL13r/1taf4BKowQcPFfr4ymblFLrL+AAsIvP0f5Dhfu8s+ybAbyJ9vdXPOZmH0frDwCNqbAXAQfP1Xq6KtwAoHs558vod+/GVTm7CV0rI8+vov+Q431EbGLqrGqZMRWeTMDB3sqiotafJLW+RAkAHtT53g2jKQzlVsjRejjwmMuIWNcu4hHL2gXQHwEHz9H6eMq1Te0A9Ky0aPe6dyMi4o1F34xmkJBjEe0HCC/L7iHYmYCD52h9PEX3BgDdKtcOLqP9bsn7fLIHi1GVh2i9hxw9vLa0/n2Dxgg42Et5orSoXccjfKgCoGcX0e/ejU1EfKxdBBxTCTne1a5jcPZw8CQCDvbVepp6bVs7AL3qfO9GRMRroynMQVmga8/M8bws3WywEwEH+2p9/4bxFAC6NMDejY92YDEnJeRYVS5jZK0/WKUhAg6erCz7ab1l1ngKAN0pTyo/Rx+z8XdZp5Q+1C4CTi2l9CZ8/jyW1h+s0hABB/toPUU1ngJAry6j/R1X93ESlrl7ExG6lw6v9e8eNETAwT7+WbuARxhPAaA7OecP0fcH+TceMDBnZe/M6+j7skqTcs49vzZyQgIO9tH6C4z2QAC6knNeRsT72nU8w8pJWIgoId+r2nUMqPUHrDRCwMGTdJCeGk8BoCtlt9Xn2nU8wyacyoQ/lSW7xrUOq/XvIDRCwMFTtb7kx3gKAN0YYKlohJOw8BcuqxzcolyYggcJOHiqZe0CHqE9FoCeXET7l8ke8s5JWLhbuayyrl3HQJa1C6B9Ag52VlLTRe06HmA8BYBu5JzPI+K8chnPsU4pfapdBDTudUxjXDxf653kNEDAwVMsaxfwiHXtAgBgF+WhwWXtOp7h5loE8IBbl1V4vmUZ64N7CTh4ita3F9u/AUDzygf0L7XreCZ7N2BHlo4e1LJ2AbRNwMFTtLy9eGMGGIBOfIm+l4p+SimtaxcBPbF09GBaf+BKZQIOdpJzXtau4RHr2gUAwGNyzpfR91LR65SSk7Cwn3cR4YHc87T8wJUGCDjYVetLff5duwAAeMgAS0XtEoBnKGNdb2L6WWI/i5zzonYRtEvAwa6WtQt4SErJeVgAmlU6IXteKhoR8ca1Mnge+zgOYlm7ANol4OBRZRlay+20wg0AmlWeNn6uXcczffIwAQ6j/CytatfRMXs4uJeAg10saxfwCOMpADSpPCT4HH0vFbV3Aw7PPo792cPBvQQc7KL1/Rvr2gUAwD16Xypq7wYcgX0cz/Ii59zz6ypHJOBgF8vaBTzg2jwwAC3KOX+I/p802rsBR1L2cXysXUenlrULoE0CDh5U5oYXlct4yLp2AQDwvXIx5X3tOp7J3g04spTSp7BPbh/2cHAnAQePWdYu4BG/1S4AAG4rrdMXtet4Jns34HSMqjzdsnYBtEnAwWOaTkc9WQKgJWWp6Jfoe6movRtwQmUfh5+5p7GHgzsJOHjMsnYBDxBuANCMQcKNiIjX9m7AaaWU1hHxqXYdnVnWLoD2CDi4Vwf7N4ynANCSi+j7YkpExMfyRQs4vY8RsaldREea7jSnDgEHD2n9Q9q6dgEAEPHnxZTzymU81zql9KF2ETBXRlWebFm7ANoj4OAhLaei23JaCwCqGuRiyiZ8sYLqnI59Ens4+AsBBw9Z1i7gAfZvAFBd+XB9WbuOA3hdnh4DlZVOKg/ydiPg4BsCDu5UFqW1/IJh/wYAVZVw40vtOg7gja5IaM6b2gV0ouWOcyoQcHCflsONCPs3AKioPAi4jP4vpqxSSqvaRQDfMqqys2XtAmiLgIP7LGsX8ICN83UAVPYl2n8Y8JjriHhXuwjgXp/CVZXHLErgDBEh4OB+Lbd7rWsXAMB85Zwvo/9wYxv2bkDTys+nUZXHLWsXQDsEHNxnWbuAB9i/AUAVOeeL6P8cbMQUbmxqFwE8LKW0jqmTg/v1HjhzQAIO/qKDc0vr2gUAMD/lHOzb2nUcwMfypQnow8eYuq64W8ud55yYgIO7tBxw2L8BwMnlnM9ijHOwV+UEJdAJoyqPWtYugHYIOLhLyymoM3YAnFTpbBwh3LgOX5KgSymlq9DFfK8OOtA5EQEHd2n5BcL+DQBOJue8iOliSu9b+rcR8cZSUeiagPJ+y9oF0AYBB3dpOeBY1y4AgHkopwc/R//hRsQUbuiChI6VMe2Pteto1D9qF0AbBBx8I+e8rF3DA7Y+nAFwCiXc+BJth/67elfa24H+fYqITe0iGjTCazUHIODgey2/OAg3ADiVi2j7PXFXq5SSE5MwiDJm9q52HQ0a4fWaAxBw8L2WF4zavwHA0eWcLyPivHYdB3AdvgjBcCwcvVvjneiciICD77Wcfq5rFwDA2HLOH2KMcGMbEa8tFYVhWTj6Vy1/j+FEBBx8b1G7gAcYUQHgaHLO5xHxvnYdB/KqLCQEBlR+vo2ffcuiUQQcfNV4W9fGUygAjqWEG5e16zgQF1NgHj7G1K3FRAcHAg6+0fKLwrp2AQCMKef8MsYJN1YppVXtIoDjKw//nI39quXvMpyIgIPbWm7r+k/tAgAYTwk3vtSu40DWKSVz+TAj5UrSpnYdrWi8I50TEHBwW8upp1ZbAA7qVrjxonYtB3AdEa9rFwFU4VrSV4vaBVCXgIPbmg04Ukrr2jUAMI6c84uI+BxjhBvbmPZumMWHGXI29hstd6RzAgIOIuLPp1it0r0BwMGUcONLjPOk77WlojB7dnFMWv5OwwkIOLjR8ouBD20AHMStcKPl972neKPLESivA1e162jAKK/t7EnAwY1F7QIeYMEoAM82YLjxycUU4Ba7OCJe5JwXtYugHgEHN/5Zu4AH6OAA4BAuYpxw4yql5MsM8KeU0iYiVpXLaMGidgHUI+DgxqJ2AffRegvAc+WcLyPivHYdB3IdEc7BAnexiyNiWbsA6hFwcGNRu4B76N4A4FkGCze2EfHKxRTgLro4IiLi77ULoB4BB5FzXtau4QGb2gUA0C/hBjBDH2N6vZirRe0CqEfAQUTbLwIWjAKwl8HCjYjpYorORuBBpYvjl9p1VLSsXQD1CDiIaDvgWNcuAID+5JzfxnjhhhOQwK4+xYy7OFxSmS8BBxFtX1DZ1C4AgL7knM9jupgyipVzsMBTlFG2Ve06KlrULoA6BBxENPwCUFrsAGAnJdy4rF3HAa1SSi6mAPswpsLsCDiIaDfgWNcuAIB+DBhuXEfEu9pFAH2a+UWVv9UugDoEHDOXc35Zu4YHbGoXAEAfBg03XEwBnutj7QIqafk7Dkck4OBF7QIe8N/aBQDQvgHDjW1MS0WFG8CzzLiLY1G7AOoQcLCsXcAD1rULAKBtg4Ybr5yDBQ5ojl0ci9oFUIeAg5bn0za1CwCgXQOGGxFT54ZwAziY0sUxuzPTOedl7Ro4PQEHzc6nuaACwH0GDjdm9yUEOIk5X1RhRgQcLGoXcI917QIAaNOg4cbHlNKqdhHAmFJK65jf5+tl7QI4PQEHi9oF3MNiNQD+YtBwY5VS+lC7CGB4v9Yu4MRaHsXnSAQcM9b4idj/1C4AgLYMHG68qV0EML7SJbapXMYptfxdhyMRcMxbyydiN7ULAKAdg4Yb1xHxrnYRwKzMaRfHonYBnJ6AY95aTjU3tQsAoA0DhxuvUkpGMoFTWsV8RsEXtQvg9AQc89ZyB4cTeQAINwAOqLzurGrXcSo550XtGjgtAce8/aN2AffxoQ+AQcONbUznYL3PAbUYU2FYAo55a7WDY127AADqGjjceJVS0qUIVJNS2sR8Pm8vahfAaQk45q3lHRwAzFTO+SKEGwDHNJcujkXtAjgtAce8tdrB8VvtAgCoI+d8GRFva9dxBO+EG0ArUkpXMY+l/n+rXQCnJeCYqZxzy90b5pIBZqiEG+e16ziCNymlVe0iAL7za+0CTqDl7zwcgYBjvlrt3ohwQQVgdoQbACe3ql0AHJqAY74WtQsAgAjhBkANZdnoVe06jmxZuwBO6//VLoBqFrULuE9KaV27BgCOL+f8IiK+xJgtxCvhBtCBXyPirHYRcCg6OACAk5tBuPGmdhEAj5nDstHGdw9yYAKO+fpn7QLusa5dAADHJdwAaMroy0Zb3j3IgQk4AICTKU/ShBsA7VjVLuDIBBwzIuCYr0XtAu7hggrAoIQbAO0py0bXlcs4phHfc7iHgGO+FrULuMf/1S4AgMO7FW6M+CTtWrgBdG70MRVmQsBBa7a1CwDgsHLO5zFwuBERr2oXAfBMVzHu5/B/1C6A0xFwzFDjm4SNqAAMpIQblzFwuJFSGvVLATAT5XXsqnYdRzLi+w/3EHDMkx9yAI4u5/w2pnBjRMINYDT/rl0APJeAg9bo4AAYQM75MiIuatdxJMINYDgppauI2NSu4wha7l7nwP5f7QKoYlm7gPv4sAjQt5zzi5iCjfPKpRyLcAMY2VVEvK1dxIHpXp8RHRwAwEGUcONLCDcAeuWaCl0TcNCSde0CANhPznkRU7gxaiuwcAMYXkrpOgYcU2n8yAIHJOCYJ6eSADiY8sHx9xBuAIxgxGsqxlRmQsAxT37AATiInPNZTJ0bo763CDeAuTGmQrcEHLTkt9oFALC7nPN5RHwO4QbAMAYdU1nULoDTEHDM06gfRAE4kZzzRURc1q7jiIQbwJyNNqayqF0Ap+FM7DyNOiMNwJHN4AxshHAD4NcY71wsM6CDg5ZsahcAwP1mcAY2QrgBMOqYCjMg4KAlm9oFAHC3GVxKiRBuANw20pjK32sXwGkIOGamPH0DgJ3lnJcxdW4s6lZyVMINgG+NdABgUbsATkPAMT8jP3kD4MDKpZSRz8BGCDcA/iKldBURXhfpioCDllzXLgCAr3LOlzH2pZSIiFUINwDuM9KYCjPgigrN8OESoA1lnPFzRCwrl3Jsq5TSm9pFADTstxh7sTSD0cEBAPypLBP9EsINAMbp4FjWLoDTEHDMz6J2AQC06dYy0dH3NQk3AHZQOqzXteuAXQk45mdRuwAA2pNzfhvjLxONEG4APNW/axcAuxJw0Ip17QIA5qosE72oXccJfBRuADzZunYBsCtLRgFgpsoy0TmMpEREvEkprWoXAdCblNJ1znkTOsHpgA4OAJihskz0jxBuAPC4de0CYBcCDgCYmZzzeUT8HuPv24gQbgAcwm+1C3iuskibwQk4AGBGyr6Ny9p1nMA2In4UbgAcxCjnYhmcgGN+/lG7AABOL+f8Iuf8e0Sc167lBLYR8SqldF27EIARlHOxXlNpnoBjflptR/aCCXAkM9u3IdwAOI517QLgMQIOWvF/tQsAGFHO+W3MZ9/GdUT8INwAOIru93AwPmdiAWBA5QTsRcxjJCViCjdelTZqAA5vXbsAeIwODgAYTBlJ+RLzCTeuQrgBcFTlNXZduw54iA4OABhIzvkspispcxhJiYhYpZTe1C4CYCZ+i4hl7SLgPjo4AGAQOeeLiPgc8wk3Pgo3AE7KjiOapoMDADpX9m18iXlcSbnxJqW0ql0EwMysaxcAD9HBAQAdyzkvYz4nYCOmM7DCDYAKyh4OXRw0S8ABAJ3KOX+IqXNjLiMp25iWia5qFwIwY+vaBexpUbsAjs+ICgB0poykfI55LXq7jqlzw5NDgLp+i4i3tYvYw6J2ARyfgAMAOlJGUua0SDRiCjecgQVog6CZZhlRAYBOlCspcxpJiYhYhXADoBkppU1EbCqXAXfSwQEAjcs5L2Lq2pjLItEbK2dgAZp0HUY+aJAODgBoWM75LCJ+j/mFG2+EGwDN+q12AXAXHRwA0KCySPQiIs4rl3JqN2dgr2oXAsC97OGgSTo4AKAxOeeXMe3aOK9cyqndnIEVbgA0LKW0rl0D3EXAAQANyTm/jXmOpFxHxA/OwAJ0w+s1zTGiAgANKCMpnyNiWbmUGlYR8c6lFICuXMf8wngaJ+AAgMrKItHLmNf51xsfU0ofahcBwJP9p3YB8D0BBwBUMuNFojfepJRWtYsAYC9GVGiOHRwAUEHOeRnTro3zupVUsY2IH4UbAP2yaJQWCTgA4MRyzh9iupKyqFtJFZaJAozDazlNMaICACdSzr9exnyXsq3CMlGAkVg0SlMEHABwAqVr433tOiqyTBRgPP+tXQDcJuAAgCPKOS9iOv861ydc25iWiV7VLgSAg1vHvMN7GmMHBwAcSc75bUyLROcabmwi4pVwA2BYPe3g+FvtAjg+HRwAcGCla+MyIpZ1K6lqHRGv7dsAGFdKaZtz3kbEi9q17GCuDxtmRcABAAdUujbeRx8f9o7lU0rpXe0iADiJ65h3oE9DBBwAcAC6NiJi2rfxLqW0ql0IACcj4KAZAg4AeKZyIeXnmHfXxiamkZSe5rEBeD6XVGiGgAMA9pRzfhlT18bc53qvYrqUYt8GwPwItmmGgAMA9lC6NpzGi/iYUvpQuwgAqhFw0AwBBwA8Qc55GVPXxqJuJdVtY+racAIWYMY6u6TC4AQcALCDnPOLmDo23taupQHXMe3b2NQuBIAmWDRKE/63dgEA0Lqc81lE/BHCjYiIVUS8Em4AcMumdgEQoYMDAO5VTr9eRMRZ5VJa4AQsAPdxSYUmCDgA4A4557cxjaSYKZ5aj984AQvAPbw/0AQBBwDcUpaIXoTTrzecgAXgMd4jaIKAAwDCEtF7vEspfapdBABtSymtc861ywABBwDknM9j6towjjLZxHQlRcsxALtyKpbqBBwAzFbO+WVMwcayciktMZICwD6ciqU6AQcAs2Mc5V5GUgDYV+vB+LJ2ARzf/9YuAABOqYyj/BHCjduuI+JH4QYAz/Cf2gWADg4AZqFcR3kfnuB8bxVT50brT94AaJv3EaoTcAAwtDKOchER55VLac02pmBjVbsQAIZgMTXVCTgAGFbO+UNE/By2un/vOqYrKZvahQAwDB0cVCfgAGA4OeezmLo2FpVLadHHlNKH2kUAMJaU0nXOuXYZzJyAA4BhOPv6oG1MXRvr2oUAAByDgAOA7uWcFzEtED2vW0mzriLijUWiABzZOjxkoCIBBwDdKgtE34Y9G/exSBQAmA0BBwBdyjmfx9S1sahbSbMsEgXg1K5DBwcVCTgA6ErOeRkRlyHYeIhFogDU8H+1C2DeBBwAdKEEG+/Dk6GHXMe0a+O6diEAzJJdT1Ql4ACgaRaI7uxTTJ0bPlwCUIuAnaoEHAA0SbCxs01MXRvrynUAAFQl4ACgKS6jPMkqpispujYAaMGmdgHMm4ADgCYINp5kG1PXxlXtQgDgRkppk3OuXca9cs4v7akam4ADgKoEG092FVO4oWsDAJ7G54zBCTgAqCbnfB7Tno1F3Uq6oGsDgB5swvs6lQg4ADg5wcaT6doAoBeb8P5OJQIOAE5GsPFkujYAAHYk4ADg6AQbe9G1AUCPNrULYL4EHAAcjWBjL5uYgo115ToAYB//rV0A8yXgAOCgbl1F+SkEG0/1KSI+6toAAHg6AQcAB+Hc67NsQtcGAMCzCDgAeBbBxrN9TCl9qF0EABzIde0CmC8BBwB7yTkvYtqvcV63km6tI+JdSskHQQBGYsySagQcADxJzvllKYFG1gAAIABJREFUTN0a55VL6dU2pq6NT7ULAQAYiYADgJ3knJcxdWws61bSNadfAQCORMABwIOcej2ITVgiCsA8CPGpRsAxmDITf/u3v0XEy1t/ysvv/zsA33Pq9aAsEQVgNlJK1znn2mXc533pSI2YlqFuPXwYi4CjY+WH82VE/COmLyDLiuUAA7i1OPQsXER5rnVMXRubynUAAJNlfPedqYQx25gCj+uI+E9EXFsC3icBRyfK09RlRPyz/F4nBnAwJTD9OaZgg+fZxHQd5ap2IQDATm6+ay1v/kDOeRvTw4rfImIt8OiDgKNh5UnqWUT8K3RnAAdWgtOzsF/jkD5GxCdLRAGgezefk84i/gw8rmIKPK6817dJwNGYW6HGT6FLAziC8jpzc+bVGMphrMM4CgCM7EVMn53OI+Iy53wVEf8OYUdTBByNKFcKfgqdGsCR5JzPYgo2lpVLGckmjKMAwPeuY/yHtTfdHRcl7PjVwtL6BBwVeYoKHJtrKEdlHAUAbrm1rHxRt5KT+rOzI+e8ienzga6OSv6ndgFzZJnfndahvRsOprzO/BTTGy6HdRVT18amdiEA0ILyueN96BK9sY2IX8KDkJMTcJyQH/ydrCLioy8O8HSlW+M8pgB1UbWYMV3HFGysaxcCAC3w/WYnq/D95mQEHCfgB38vq/BCADspuzX+Fbo1jmUb0+vRp9qFAEALfL/Zy6eYPk/o6DgiAccR3ZpBO69bSdfMuMMdyuvLeditcWw+jABAUT5/XIZgY1/biPglpfShdiGjEnAcwa2lfj+H5aGHsI2pLXxVuxCozcWlk7FnAwCK8v3mfUzfcXi+TbjCdhQCjgMr7VqX4YnqMaxjeiG4rl0InFLO+WV8XUwsND0uezYA4JYyCnsZPoMcgwcqBybgOJCSal6Gyyin8FFbF6MrLaBnYWHoqWxiem1ZVa4DAJrg+83J2PV1QAKOAyhdG59DqnlK1zGdldXNwTDKB4mzMIJySs64AcB3dG1UsY7p+82mch1dE3A8U875Isyi1fRO2knvyoeIn8ITklOzQBQAbtG1Ud02ppDDbo49CTj2VNrHP0fEy8qlMM2uvfElhZ7cOu1qr8bprcIZagD4Rtn59TmMxrZgFdODXN9vnkjAsQctW03aRMRrIyu0rIyz3XRqeP04vXVYVAwAf1GutF3WroNvXMf0/WZTu5CeCDieKOf8IaYTSbTHOVmaU56G3IQai7rVzNY6po6NdeU6AKA5OefLiDivXQd32sYUcqxrF9ILAceOyjzaRfjh74ErK1RVOjVuxk8WVYuZt024MQ8Adyrfbz6HxeY9eOMh7m4EHDsoP/xfwr6NnqxSSm9qF8F82KnRlE04+QoA97JPsEu+3+xAwPEIP/xdW8fU0mU5Dwd366TrP0Oo0YpNCDYA4EFlfPZL+OzSIyHHIwQcD/DDP4TriHgl5OAQSuC5jK+dGrRhG1Ow4WQ0ADzA95shuCD5AAHHPfzwD0XIwd7Ka8HN+IlOrrZsI+KXiPjk5xsAHub7zVB8v7mHgOMOfviH5EWAnZTRk2VMgcYyLAltkWADAJ7A95sh+X5zBwHHd/zwD82LAHcqP/fL+Bpq0CbBBgA8ke83Q7tOKf1Yu4iWCDhuKU9u/wg//CMTcvD9gtBl6NJonWADAPYg3JgFi0dvEXAUTsHOylVK6XXtIjitnPMyvnZo+DnvwyYiPsb0MyvYAIAnKMvRfw/hxhwIOQoBR5Fz/j186ZkTLwKDu7Uc9KZLg35swrlXANibh7ez9DGl9KF2EbUJOCIi53wZEee16+Dk3jkrOY5bezRuAg1PK/qzCcEGADxbzvlLeMAzR2/m/jlq9gFHzvltRFzUroNqXqeUrmoXwdOVkZNlRPwjBBq9W0fEL34WAeD5PLydtW1M+wavaxdSy6wDjvIF6UvtOqhqGxE/ppQ2tQvhfqXN8vsODfq3jqljY125DgAYQs75PCIua9dBVZuYvt/Mcn/ZbAMOS3e4xWWVxpSfz2V87c4wPzqWVUwdG7N9ugAAh+ZiCresU0qvahdRw/+rXUBFn8MPP5OXEfE+It7VLmSO7ujOeBl+Nke0ja/BxqZuKQAwlvJ56jJ8hmKyzDl/mOPS0Vl2cOScP8T0hRZus4/jBMpo2Mv4GmYsatbD0W0i4teI+KRLCgCOw94N7vHj3DpmZxdw2LvBA7YR8YMvYYfxXWfGP2IKMoyazMd1TN0aq9qFAMDIcs5nMXWnw/c2MbN9HLMaUbnVugV3ufn343XtQnpTZj4X8bUzYxE6M+bqKqZgY127EAAYne83PGIRMxvFn1UHR875IiLe1q6D5hlVuUdZ/rmIqSvj77d+zbxtYwo2PtqvAQCnk3P+HBFnteugea/m8vBpNgFHecL8e+06KrmO6QvIb+U/b8sf+96y/P7/s3c/13EcydqH35kze2EsmJYFgha5VtMCgRYIsICEBSAtAGgBmhYQsoCldS4EWaAaC74eC+63qGyy2UT/r6qIyPo95+hcSVcCY0SgOvOtyMgf9HU2wmzY0lya/FGVjY4Mggxs00r6IGkx5Z8XAAAsTPxoyqn7m6kOs281kaMqUzqiMpXWraWkRt0P+/ORSd13/+wLN1zMz6wvggtJ95JurAsZUgkxLtT9nk491MJxGnXHUOh0AgDAQFmj31vXMZL1/U1z5NDMZvNvTHR/M1N3kuGdbRnDm0QHR875rep+AKzaw38fY8NR0uJf1bXD1ZyAhm7lWjtOsvrjPxt/DRyDa14BAHBiArdCtur2Nx+HvgWkBB7r+5ua/Vj7Oq76gKN8w/6tOjfijQzfoq49DN6oztsxnlNKP1sX8ZK17otVAi11CbQ03dY7DONZ3TGUpym0NQIA4F15ifW3dR0DWagLNRqLX7zsb67V7W9mFjUM7CmlVPWFClMION6pvnRzIWfD/Mr1u3eqr8XrZoxrLte6LVbma3++Ci7WwwxgSKuusA9TuzsdAADvKhws6rJLtOL9Tegu9X2qDjgqTDcXchZsbKrwQdDquFkc27onftr4+4QV8KgVQ0MBAHCrrLU/W9fRo4WkW8/rjvLf/F71rN2blNIr6yKGUnvA8aiuxSi6Z3U/+I11IYcqczruVWdrF1CTVbeGWTsoAAA4TM75s+p4kdio65Rujes4WM75Wt3+poaj4KN0qVuoNuCopHtjqa5j48G6kFOUM2x36ib2AvCF2RoAAARSSffGUt3mOuRNbGV/86j4R4TalNKP1kUMoeaAI3r3xrO6H/7w59/Lw/hRdHMA1lZnXAefSA4AAPpVQfdGI+l1DS9WSrf6o2J3c1TZxVFlwFFB98YipXTM3Af3Str5SbEfykBUq2ukF9aFAACA41XQvfE+pfTOuog+lT3nJ8WdzeH2xshz1Bpw3CvusYgqk7SV4L83QCStvh5BaW1LAQAA5wjcnR76SMohAv/eSBXeqFJdwFE6Bf5WvHahpbqWrca6kKGVAT2P1nUAFeIICgAAlQncnb5Ut4Gufk2Sc36nbvZgNE8ppdfWRfTpn9YFDOBKMcON6tKzbUqHSlVHcABjC3UB6b9TSrdTWEgAADAhb6wLOMFkwg1JKsdvIu5vrkqAVo0aA45oydmkfvhXCDmAsz2p+xn6d0qp6tZPAAAm7tq6gCMtJf3I/iaMiAHaVlUdUQk4fGeS4cY6jqsAR3mW9FHM1QAAYBICrpXZ3+T8VtK9dR1HWKaU/m1dRF9q6+D4zbqAI1VxDew5StJ5a10H4Nizup+RH1NKP6eUHgg3AACYjF+tCzjC5MMNSUopPag7PhzFRbn2tgq1dXD8P8WZv3Fbvvmh8NOHgb616o6gfCDMAABgmgIOF33Nkdmvcs6fJc2t6zhQNcNG/2VdQF9K6hQl3FgQbnwrpXSTc75U3HukgXO16kINbkABAABSd3lCFO8JN77zWtKfkmbGdRziKud8kVJaWhdyrmoCDsVp32rFkYxtVg+BKEEVcC5magAAgG2iHL9vyi0iWJNSWuacV/ubCK4U62jNi2qawREl4XxdQzI2hLLBizh5GDgGMzUAAMBO5XhKhM7mpVi/b1W6ct9b13GgKA0DO1URcAQ6nvKe1vPdSmsb7W2ozepKV0INAABwiCgvb9+zptmtdLdE2ANG+Z7bqZYjKr9YF3CAVhJzNw5zq24gT4TQCnjJUl2o8bu6tk26tgAAwDEi7G8a5goe7EYBjqrknK+iz1KpJeCIkDbdssk5TEqpzTl/kHRnXQtwhGdJjRgSCgAAzhdif2NdQBQppeec80L+b438RcG76cMHHOV82sy4jH2a6EmYgQd1g5VmxnUAuzxJ+kMMCQUAAD3JOc+tazjAghc6R7tVF1x57lKfWxdwrhpmcMytCzhAlMEybpRuF/67wZtWXfj2OqX0j5TSa+ZpAACAns2tCzgA6/Qjlf3NB+s69rjMOXsOYPYK38Eh/+fTmpRSY11ERCmlRc75TnRxwM5S3bETujQAAMBYvO9vFqyJTvYg6Y38d3GEPX1QQ8Dh/fok0s3zfJB0b10EJqVRF2gQTgIAAAtz6wL2YH9zopTSMuf8JN+zOELP4QgdcJT2Gc8BR8sG6WwLdcNGPaeciG01HHQVajAMGAAAmMg5e97bSN1aqbUuIrj38h1weP8e3Cl0wCH///G9n7FyL0jKiVhadYEGV7gCAABvvO9vPloXEF25MbKR306duXUB54gecMytC9gjbGuPMx9FwIHTrTo0/hJvHQAAgG8/WRewwzKltLAuohIf5Xgvm3O+jHpLTvSA4z/WBezAQMKepJSanHMrho3iMBw5AQAAUXnu4ODlbX+eJD1aF7HDTN2aOpzoAcfMuoAd/rAuoDJPkt5aFwF3luoevgwFBQAANfAccPxuXUAt1o7hX1nXssWlggZa0QMOzw+AkN8Qjv0hAg50YcYq0HiO2joHAACwhdvB+ikl9jf9+kN+Aw7PJyV2ih5weH0AtBxP6V1jXQBG16oLM/5S9/v/zHETAABQq5zz3LqGHRrrAir0JOneuogtZtYFnCpswMEDYFpKG1cjx8N4cJb1oybPYnYGAACAJxy/71m5TWUpny/tPZ+U2ClswOHcX9YFVOpZBBw1aEVnBgAAwKa5dQE7NNYFVKqRz2MqHkOXg0QOODynSswFGAbBUTyNup+H/6oLMhrTagAAAHAK9jfD+Es+Aw7lnC8ivoSMHHC4TZXYxA2mtS4AWzXqfn/+u/pz5tAAAAAc5QfrArZYRtzoBuE5OLpUwM6dyAGHV/zwDySl1OScrcuYstWcjFYEGQAAAH3z2qHueRMeHXvHnkUOOLwmnDwAENkqxFiqa5l7VpfaN5ZFAQAAwAyb8IHwArd/kQMOrwknhvUsfu/P1ZT/+8f6XxNiAAAA4AXMwZummXUBp4gccHhFB8ewSJB3W3VftOqOkaw6MggwAAAAABxqZl3AKQg4+vc/6wJQnWbtz5/19Xts9feXKSWCNQAAACCeRr6vCA6FgAMYzpfuiQ2tuu6Kdc36v0dgAQAAAADHIeAA+vE+pfTOuggAAAAAmKp/WhcAHIkZHAAAAMB46CxGGAQciIYpzgAAAMB4eMGIMAg4EI3XB2xjXQAAAABC89op0VoXAByKgAPR8OAHAABAjTx2Ki9TSq11EcChCDgQSkqpkb8ujpYHPwAAAM7UWBfwgsa6AOAYBByI6Mm6gA3e6gEAAEAw5YVZa1zGpt+tCwCOQcCBiD5aF7Dhg3UBAAAAqIKndeVSvMhDMAQcCKccU2mNy1hpOJ4CAACAnizk5zj2h5SSl1qAgxBwIKpb6wKK99YFAAAAoA4lUPDQxbGU9GBdBHAsAg6ElFJ6kv3Qo4fSTQIAAAD05UH23crv6d5ARAQciOxGdi18rejeAAAAQM9KsHBjWEKTUqJ7AyERcCCsMvvitcEvvZT0mlQbAAAAQyhdwhYhRyub9TXQCwIOhGb08H+dUnoe+dcEAADAhKSUFuqGjo6Fl3gIj4AD4ZWH/xghx1LSz8zdAAAAwBhSSjca51h0K+kVL/EQHQEHqlBCjp813ECmZ/HQBwAAwMhSSu/UHRsZqrOiUfcSj3UuwiPgQDXKQ/ln9X+l1fuUEg99AAAAmCg3CP4s6anHL7uUdJtSesWxFNSCgANVSSktU0q3kn7UeWcWl+Xf/7Gk5gAAAICZlFKbUnot6ZW6rotTLdUde/mR21JQm39ZFwAModywcpNzvpV0JelXSXNJFzv+taW6D4vfJT2RZAMAAMCbMg+uyTnP9O06d5dWZZ1bukGAKhFwoGolpFiUP1Q+CGYv/KPPBBoAAACIorzQeyh/bF3nMiAfU0LAgUkpHwStcRkAAABAr1jnAszgAAAAAAAAFSDgAAAAAAAA4RFwAAAAAACA8Ag4AAAAAABAeAQcAAAAAAAgPAIOAAAAAAAQHgEHAAAAAAAIj4ADAAAAAACER8ABAAAAAADCI+AAAAAAAADhEXAAAAAAAIDwCDgAAAAAAEB4BBwAAAAAACC8yAHHzLoAAAAAAADgAwEHAAAAAAAIL3LAAQAAAAAAIImAAwAAAAAAVCBywLG0LgAAAAAAAPgQOeB4ti4AAAAAAAD4EDngAAAAAAAAkETAAQAAAAAAKkDAAQAAAAAAwiPgAAAAAADAxty6gJoQcPTvJ+sCAAAAAACYGgKO/l1YFwAAAAAAwBlC3loaOeD4w7oAAAAAAABOkXP2/HJ8aV3AKSIHHF7NrAsAAAAAALh3aV1AbQg4+jezLgAAAAAAgKmJHHCEbJkBAAAAAEC+X44zg2Nkbv+D55xpNQIAAAAA7DKzLmCblFLIhoLIAYdnnofFAAAAAADs/WBdQG0iBxytdQE7zKwLAAAAAAC45rXz3+1piX3CBhwppda6hh1m1gUAAAAAAFzz2vkf8niKFDjgcO4/1gUAAAAAAFzz2sFBwGGksS5gi5l1AQAAAAAAn3LOM+sadvjLuoBTRQ84vPKaxAEAAAAA7M2sC6hR9IDD6/CTi5yz1/NUAAAAAABbnl+KN9YFnCp6wPE/6wJ28PwNCwAAAACww9zGAUQPOLx2cEgEHAAAAACAl7ndL6aUGusaThU94PA83ZVEDgAAAADwEq8Bh+c99l6hAw7nyZLXb1gAAAAAgJEyr9HrzEbPpyT2Ch1wFF4Tprl1AQAAAAAAdzy/DG+tCzhHDQGH24TJ+d3GAAAAAIDxza0L2OG/1gWco4aAo7UuYIe5dQEAAAAAAFd+si5gh8a6gHPUEHB4Tpg8f+MCAAAAAMY3ty5gB68jIA5SQ8DRWBewg+ezVQAAAACAEZUxBl4HjCql5HYExCFqCDg8/wbMrQsAAAAAALgxty5gh8a6gHOFDzhSSks5bqPJOc+tawAAAAAAuOB5jEFrXcC5wgccBV0cAAAAAADv5tYF7PCXdQHnqiXg+MO6gB1+sS4AAAAAAGAr53wh33MaPTcOHKSWgMPzb8TcugAAAAAAgLkr6wJ2SSk11jWci4BjBDln19/IAAAAAIDBee7ud72nPlQVAUdKqZXjQaPy/Y0MAAAAABje3LqAHQg4nGmsC9iBDg4AAAAAmKic86WkmXUdO3iea3mwmgIOzxNfZznnmXURAAAAAAAT3l9608HhTGNdwB7ev6EBAAAAAMP41bqAHZYpJQIOTwJMfP3NugAAAAAAwLhKN7/n62Eb6wL6Uk3AUTTWBexwyTEVAAAAAJgc7938VczfkOoLOLz/xnj/xgYAAAAA9Mt7N39jXUBfags4GusC9vD+jQ0AAAAA6EmA4ynVzN+QKgs4Aszh4JgKAAAAAEzHtXUBezTWBfSpqoCjeLIuYI831gUAAAAAAEbhvYvf+5iHo9QYcHj/DWIOBwAAAABULuc8lzQzLmMf7w0CR6kx4PD+GzTLORNyAAAAAEDdvHdvtCml1rqIPlUXcJTfoNa4jH28f6MDAAAAAE6Uc76Q//kb3psDjlZdwFF4/426YtgoAAAAAFTr2rqAA3gf73C0WgOO360LOADDRgEAAACgTt73e8uUkvfGgKNVGXCU62KX1nXscV3algAAAAAAlSgzF2fWdexRXbghVRpwFN5/wyKcyQIAAAAAHMd794YU49TD0WoOOCL8hkX4xgcAAAAAHKBcDTs3LmOfKo+nSBUHHOU3zPsxlVnO+dq6CAAAAABAL+6sCzhAleGGVHHAUUT4jYvwAwAAAAAA2CHnfCn/3RtSjNMOJ/mXdQED+13+51zMcs7XKaWFdSFTUAa7Xpa/vCh/tOWvn1NK3rt+AAAAgBeVDfbqIoNLSc/lz5cppeeX/y306N66gANUezxFkv5hXcDQcs5/y/8E2zal9KN1EbUqU4x/VZemzvb8462kRtLvNf/gAwAAIL7y8u5a0i+Srg74Vxp1L4GfUkrtYIVNUJm98dm6jgMsUko31kUMZQoBx72kt9Z1HOA2pfRgXURNynyTO50ecLWSPkp6oLMDAAAAXuScZ+rWuddnfJknSR9SSk0PJU1ezvmzYhxPeVXz7/kUAo6ZpL+t6zjAUtKPbKTPV9LTR/XXudOqC6Do6AAAAICZ0rFxp35f4D5JumEfcrpA3RvVnxyofcioSutVhPNmF4rRaeJa6dj5rH6PJc0kfco5P5YPFQAAAGBUZb7Gn+p/z3Al6e+yScdpHq0LONBH6wKGVn3AUXywLuBAd6XjBEfKOV+UtrAhQ6JrSZ8JOQAAADCmMlOu75d46y7UrXOvB/r61co5v5X/mY8rC+sChjaVgONJ3RGQCKKkf26UwGGsM2+XIuQAAADASEro8Elfb0cZ0iMhx+HWjgxFMInBspMIOMp5sijzE+YlocXhHvX16tcxXCrGGTsAAAAEVo6ljH31KCHH4R41TvDUh+qPp0gTCTiK99YFHIFZDwfKOb/TYVdi9e2yzPsAAAAAelf2A2N1bmy6L+EKtigzS6K8mG6ncmHCZAKO0o7TGJdxqAtxVGWv8tC1bAl7yzAmAAAADORedrMdLjR+50gYJXyKtF+LMpPybJMJOIpIv7FXHFXZy8NDN9KDDQAAAAGUl2jXxmXMOaqy1Z3iDBZdagLDRVcmFXCUtpzWuo4jcFRli/LQnxuXIUkzHvwAAADomZfBlV7qcKO8hB7y5sa+PZWZlJMwqYCjiDSLY3XuDt97Y13AGk+1AAAAILCc80w+XuRJvMz7RsCjKVKs/e/ZphhwRLoyVupawyIlhIMrDxZPx3cuywcRAAAAcC5vL89+tS7AEauhr6daTOFq2HWTCzhKe06kWRwSU4w3eQo3VjzWBAAAgHi8rSu91WOi3N44Ny7jWNH2vWebXMBRPChWF4ckfWYexxe/WBfwApJtAAAAnKV0Bc+My/jO1G8OLHM3os0jaVJKz9ZFjG2SAUfQLo4LSZ+ti3DCYzeLx5oAAAAQy8y6gC3m1gVYKZ300eZuSBObvbEyyYCjiNjFcZlzjvjD1TePYQLdNQAAADjX3LqALX6wLsBC6aCPNndD6ro3GusiLEw24AjaxSFJ1+X8F5xhTgoAAAAqNbl1bgk3PstvV80uk+zekCYccBQRuzgk6Y7rmlyKluwCAAAAeNm9YgY7T1Pt3pAmHnCULo6o6dYjIQcAAAAA9KuMBbi2ruNEt9YFWJp0wCFJKaUHSa11HSci5AAAAACAngQPNxYppda6CEuTDziKqF0cEiEHAAAAAJwteLix1MS7NyQCDklSSmkhqTEu4xyEHAAAAABwouDhhiR9KCMYJo2A46voaRchBwAAAAAcqYJwo00pvbMuwgMCjiKl9CxpYV3HmR5zzm+tiwAAAAAA73LOFznnz4odbkjxX9b3hoDjW7eKeW3suvuSQAIAAACI5QfrAqYi53wh6bOkuXEp52pSSk/WRXhBwLGmnFmqIf26zjn/WX5oAQAAAMRwaV3AFtFfAn8j53wp6W/5/e99qKWkG+siPCHg2FDBwNGVS0l/lh9eAAAAADjVX9YF9KXMLfxTUg0vgz9M/VrYTQQcL6slBZupCzmYywEAAABgssq8jUdJtRznf2aw6PcIOF5QUrD31nX06D7n/IkjKwAAAACmpnS11zBMdF0tL+V7RcCxRUnDnq3r6NGVpL9zznPrQirGcSAAAADAkZzzO3VHUmpaq78vt4BiAwHHbjeqa6DOhaTPOef74N0cXn+YI/83BQAAAKqRc77MOf8p6c66lp5xNGUHAo4dSipW01GVlbfqujmurAs5UU2hEwAAAIAeVdq1IXFryl4EHHuklB4k1Xiv8IWkTznnzznnmXUxAAAAAKrbkI8q5zzPOf+t+ro2VjiasgcBx2FqO6qybq6umyP6sRUAAAAgOq/rcdd7oZzzLOf8Wd0g0ZlxOUN5Ki/fsQMBxwFSSktJr63rGNjq2Mo7gg4AAAAAa1x2DZRg41HS3+pe3NaqFUdTDkLAcaCUUqM653Gsu1DXzkXQcZpfrAsAAAAAarcRbFwblzOG1+WlO/Yg4DhCmVZb4zyOTd6DDpcJMgAAAIDhTDDYkKRb5m4cjoDjeDfqWoSmYD3oeHQ0jPR/1gUAAAAAfco5M2B0izI89JOmFWxI0oK5G8ch4DjS2jyOKbUIXah7kPxdbl25ti0HAAAAqI63rul1o+99cs4XOee35VaUz5Kuxq7B2LOkW+siovmXdQERpZSec843kj5Z12JgLmmec76XtJD0kZapL0jdAQAAUJ0x1/s55ytJv2panRqblpJeMXfjeAQcJ0opPeWcbyXdW9di5ELdzStvc86tutkkY4UdXn/QPafuAAAAgEtrocaVWFMTbpyBgOMMKaWHnPNPmna6KHV3TW+GHb+Xm2eGQMcIAAAAajO3LmAs5RKDuQg1XnJDh/zpCDjOlFK6yTlLhBwrM30NO5aSGkl/SGqm8IOac74gbQUAAEBFelnb5pzn6kKNXzShMOdINymlKdzaORgCjn7cqpu/wAyGb12oS2SvJGkj8Hju74gAAAAgAElEQVQesMPD0qW6/40AAABADY5+SVk6NC5FoHGM9ymlhXUR0RFw9CCltMw5v1I33ZeQY7vNwEPqHpjPkv5a/fkBHRDtcCUCAAAAJv5jXcApcs4zdV3cc0k/qdsPzcwKimmRUnpnXUQN/mFdQE1KUknIcb6lurCjlfTf8udLrYUfOef/M6tut1eVdqYAAABgQDnnz/LZ6dBIulEXWqz++I++hho4zyKldGNdRC3o4OgRnRy9WQ0d+k7p+mhHrOVYc3FEBQAAAPWYS/rbuohKEW707J/WBdSmdBi8Ejd9DGlmXQAAAADQM16QTgvhxgAIOAZAyDFpP1gXAAAAgJC4KnU6CDcGQsAxEEKOySJ5BwAAALAN4caACDgGRMgBAAAAYJ+c89y6BoyCcGNgBBwDWws5GuNSMA46OAAAAABsuiXcGB4BxwhSSsuU0itJC+taMDjOTgIAAOBYM+sCMKiblNKDdRFTQMAxopLYvbeuA8PKORNyAAAA4Bgz6wIwiKWkVymlhXUhU0HAMbKU0jtJtCbVjWMqAAAAOAY38dWnVRduNMZ1TAoBh4GS4P2sLtFDfejgAAAAwDF4QVaXRtLPKSUumxgZAYeR8s3+o7hhpUZ8QAEAAADT9JBSelUum8DICDgMleGjP0ti4AwAAAAwXXPrAnC2pbphorfWhUwZAYcD5YfgtTiyUotfrAsAAAAAMJpnMUzUBQIOJ1JKT+rmcnBkBQAAAJiInPPcugac5UFduME+zoF/WReAr1JKraSfc87vJN3ZVoMzzK0LAAAAADCo1ZGUJ+tC8BUdHA6Vq2RfqbtaCAHlnLlJBQAAAIeYWxeAoz1J+pFwwx8CDqfKfckMII2Lm1QAAABwiB+sC8DBlpJep5Rec0uKTwQcjpVbVm7VdXNwpisWOjgAAABwCF6MxUDXRgAEHAGklJpynex7cdNKFHxQAQAA4BAz6wKwU6tuiChdGwEQcARSZnP8rC49hG//sS4AAAAAIcysC8CLlupeMP9cxgcgAG5RCabctPK6XCf1KB6IXs2sCwAAAIBvOWe6fn16knRb9l4IhIAjqJIi/phzvpZ0L2Y+eDO3LgAAAADusYb3pZH0no6NuDiiElxKaSHpRzGfwx2uigUAAMAec+sCIKmbs/E6pfSKcCM2Ao4KlNtW3omgwxtaDgEAALALc9tstZJuUkrcjlIJAo6KEHS4Q8ABAACAXWbWBUxUq6/BxsK4FvSIGRwVKtcXvcs5P0h6K+k38fC0QCIPAACAXebWBUxMI+kD3Rr1+od1ARhHGUb6RnQVjKlJKb2yLgIAAAD+5Jxnkv62rmMiFpI+Ml+jfnRwTERpvVqU62V/k3RtWc9EECYBAABgm5l1AZVrJX2UtOC61+mgg2Oiyg0f1+q6OmamxdTt3+XIEAAAAPBFzvmdpDvrOir0pK5bg2MoE0QHx0SVTfeDpIec86W6oONK3MXdt0t1Z/0AAACAdcxr68+zum6NJ7o1po2AA0opPUu6kXSTc76S9KsIO/pCwAEAAICXzKwLCK7V126NZ+Na4ARHVLBVzvle3S0sON0ipXRjXQQAAAB8yTn/n3UNQT1Jek+ogZf807oAuPa7dQEVmFkXAAAAAF/KEXGc5gPhBrYh4MAuDMc839y6AAAAALhDwHE69ijYioADW5GM9oOEHgAAABt+si4gKvYo2IWAAxgeAQcAAADWsT48Dd0b2ImAA/s01gVUgIQeAAAA6wg4TkP3BnYi4ACGxwcYAAAAJEk555mkC+s6gBoRcGAfzylps/ZHa1jHPnPrAgAAAODG3LqAHVr57uD2vDeBA/+yLgDu/c+6gG1SSq/W/zrn/KecdkvknC8ZiAQAAAD5Pr78MaX0Luf8f9aFbOF2bwIf6ODAPpEG+bTWBezgMngBAADA6DyvCxvrAvaItDeBAQIO7OO26+CF61f/MinkML9YFwAAAAAX5tYF7PD8whrbE7d7E/hAwIHINoczNRZFHMjzBwUAAABG4Dw8aFNKSzEAFYERcGCf1rqAI3hOdC9zznxYAAAATNvcuoAdPK+lV1rrAuAbAQd2Sim11jXsMFv/i5I4tyaVHMZzYg8AAIDheR4wujruPbMsYhfnexM4QMCByGYv/D3PyfPcugAAAACYmlsXsENT/u/MsAbgLAQcOESkacUMGgUAAIA7OeeZfIcHnl8USrH2JDBCwIFDeH/YrWusC9hhbl0AAAAAzHg+rrwaMOpZpD0JjBBwILKXzjC6fvA5n5wNAACA4Xju5l1fQ3ueEwLsRMCByL67laQkz55Djrl1AQAAADAxty5gh/Vj3tz8h7AIOHCI1rqAI3kOODwn9wAAABhAzvlCvo+oNNYFHKC1LgD+EXDgEP+1LuBIf1gXsMPcugAAAACMbm5dwC4ppca6hgNE25PAAAEHauS5g+OCORwAAACT47mL1/PaGTgKAQcim7/0N1NKz/J9jdTcugAAAACMam5dwA7Nxl/PDWoAekHAgVp5TqI9J/gAAADoUYD5G56PdwNHIeDAITyHBdt4flDPrQsAAADAaObWBewRZa3fWBcA/wg4cAjPxz22aawL2OEi5zy3LgIAAACj+NW6gB3alFJrXQTQFwIOVCnAJOi5dQEAAAAYxdy6gB0a6wKAPhFwoGaNdQE7MIcDAACgcjnnmaSZcRm7eD7WDRyNgAM18/zAnpeBUwAAAKjXlXUBe3wzf4P1KaIj4EBoe2ZZeB+YNLcuAAAAAIPy3LW7TCltrpc93/YC7EXAgZo11gXs4XngFAAAAM43ty5gh8a6AKBvBBw4hPdOiBellJbyXfvcugAAAAAMo3Qaez7y4fk490s8r+vhBAEH9ipBQVS/WxewwyznTBsgAABAnbx36z5ZF3CM4HsSjISAA7VrrAvYY25dAAAAAAbhecBom1JqrYsA+kbAgaqllBrrGvbwnuwDAADgSAGuh22sCwCGQMCBKWisC9iB62IBAADq47l7Q4o3fwM4CAEHpsDzHA7J/wcgAAAAjuP5eljJ9wtA4GQEHJiCxrqAPTimAgAAUInSnev5BdYz8zdQKwIOVC+l9CzJ89TluXUBAAAA6I3ncEPy//IPOBkBB6bC8zVYFzln7x+EAAAAOIz37lzmb6BaBByYCu8Pcu8fhAAAADjM3LqAXVJKnl/8AWch4MBUNNYF7EEHBwAAQHClK9fzDXmEG6gaAQcmoQxSerauY4eLnPPcuggAAACcxXtXrveuZuAsBByYEu/XxXr/QAQAAMBu3rty6eBA1Qg4MCXeH+jePxABAACwRYDjKS3Xw6J2BByYjADXxc5yzpfWRQAAAOAk3rtxvb/sA85GwIG9Ktt0e3+w/2ZdAAAAAE7ivRvX+3HtnSrbk2AgBBw4hOdWu2N5f7BfWxcAAACA4wQ4nrJMKTXWRZzJ839fOEHAgeiOvRmlGaKIHl2UD0gAAADEUcvxFM+3DgJ7EXAgtJTSUTM1yj/v/ZiK9w9IAAAAfMv7C6qDroc9dm0NeEPAgSnyfkzF+wckAAAAipzztfwfn/D+gg/oBQEHpqixLmAPjqkAAADE4b379onODEwFAQcmp9z/7f18IbepAAAAOJdzvpD/7lvv3ctAbwg4cIgar2T6aF3AHlflAxMAAAB+eQ83pHqOp9S4J0HPCDhwiBo32hEe9BE+MAEAAKbsjXUBezxXdDylxj0JekbAgciaU//FIMdUvH9gAgAATFbOeSb/XQWndC03fRcBjIWAA1Pm/ZjKZfngBAAAgD8RXkZF6FoGekPAgUP8YF3AQCI88CN8cAIAAEyR9+PEz6VruRa17knQIwIOHMJ7691JghxT8f7BCQAAMDk55ytJM+s69vDerXysKvck6BcBByJre/ga3h/8s/IBCgAAAD9+tS7gAKd2K7d9FgGMiYADkf23h68R4ZjKb9YFAAAAoJNzvpB0bV3HHs0Zx1P6WGMDJgg4cIiZdQFDiXJMhWGjAAAAblxbF3AA713Kp5hZFwD/CDhwiJl1AQOL8AFwbV0AAAAAJMUYAh+hS/lYM+sC4B8BByLrq/Ni0dPXGRLHVAAAAIzlnOfyv9F+Siktz/j3vXc3A1sRcGAn50cjznlwf1E+ALyn3AwbBQAAsBfhpdPvZ/77vayxh+B8bwIHCDiwz8y6gJGc+0EwhggfqAAAAFUKMlx0mVJaWBcxoJl1AfCNgAORtT1+rSc5TqsLho0CAADYeWtdwAH66Epue/gagAkCDuxzaV3ANmdcffXS14pwTEXy/9YAAACgVhG6ac8ent/nGnsAbvcm8IGAA/tcWBcwogi3qUSY2g0AAFCVMgttZl3HHm1KqbEuYmBT2pvgBAQc2OcH6wK2aPv+guUDofev27OLnPO1dREAAAATE+ElU5/dyG2PX6tPXvcmcIKAA/t4bQNrB/q6dHEAAADgizIDbW5cxiE+9Pi12h6/Vp+87k3gBAEH8K2FdQEHuCx3sAMAAGB4d9YFHKBxPjsDGAUBB/aZWxewxR9DfNHywdAM8bV7FmHIFQAAQGjlatgr6zoO0HcX8iBr7R7MrQuAbwQcwPciHFO55spYAACAwb2V/8GWUW4DBAZHwIGtcs6ez7i1Q33hlNJC3QeFd9fWBQAAAFQuQtfsU0qp77Vr2/PX643zPQqMEXBgF89pdTvw14+Qgr8pbZMAAADoWbm5bmZcxiH6HC660g7wNfvC+hdbEXBgF8/p6NAdFkN8UPTtQnRxAAAADCXCzXXPKaXnAb6u525mz3sUGCPgwC5u09GBHuSbX3/QX6MnET54AQAAQik31kXYSA/yUm7otfaZ3O5RYI+AA7v8Yl2AsQhdHLPSPgkAAID+RLgadqrDRae+R8EOBBzYxWs62oz06zzJd3veCl0cAAAAPSlDLOfWdRxgiOGi65oBv/Y5vO5R4AABB3aJ0JY3mPKBsbCu4wCXpY0SAAAA54vy8ihCt/EQJr1HwW4EHHhRznlmXcMOf4z4a0X54IjQRgkAAOBaWQNfG5dxiKGGi64bc819FOd7FRgi4MA2M+sCdhjt2EhKqZXf9rx1c7o4AAAAzhblpdEYL+E8H9WeWRcAnwg4sI3n1q+xpzp/HPnXO1WUD2QAAAB3AnVvLFNKixF+Hc83qXjeq8AQAQe2+Y91ATu0Y/5i5QNk1F/zRPMyFAsAAADHi/KyaKwj1O1Iv84pPO9VYIiAA9u43SiXYyNji9LFEWUoFgAAgBs55wtJV9Z1HGgxxi9itOY+lNu9CmwRcGAbrw8Nq1a5B6Nf91jXDF0CAAA42lvFuH50MXLw4PWYite9CowRcOA7JcH2+oA3GXYU6MpYKU57JQAAgLmy9o3SBTt2V7HXQaMX5fcN+AYBB17iORG1vK7qveGvfQy6OAAAAA4XpXvjOaXUjPxrur0qVr73LDBCwIGXzK0L2MEsRQ50ZaxEFwcAAMBewbo3xhouus5rB4fke88CIwQceInnqcTW5wDp4gAAAKhHlO6NdqSrYTdZr7138bxngRECDrzEc7uX6UO2tAW2ljUc4dG6AAAAAK/Ky6AoXa9WN/p5Djg871lghIADL/H6sFiWYZ/WonRxzHPOc+siAAAAnIoSbixldKNfWXt7WH+/xOueBYYIOPAN5xtiFwlyaQ9sjcs4VJQPbgAAgNGU7o1r4zIOtTB+yediDf4S53sXGCDgwCbPSainh6tVm+Cx6OIAAAD4XqSXQBbDRdd5WoNv8rx3gQECDmz6ybqAHf6yLmDNg/y2622K9AEOAAAwqIDdG61xDZ7W4Js8711ggIADmzynoK11ASulTdA6TT/UPOd8bV0EAACAE5EGsXuY/dZaF7CD570LDBBw4ItyD7jbh0S5wcSThXUBR6CLAwAATF45ujs3LuNQjYPuDY9r8HVu9y6wQcCBdZ4fEO7O/pUPnIVxGYea0cUBAAAQ6qWPh+6NFXdr8RXmzWEdAQfWza0L2MHrQ9XTB88+96VLBwAAYHLKy565cRmHapx1Tnhdi0txfk8xAgIOrPvFuoAdXA43CtbFcSHprXURAAAARujeON1/rQvYwfMeBiMj4MA6jqicxtsH0C5vyuRwAACAycg5v5M0My7jUN66NySpsS5gB897GIyMgAOSpJzzpbo3/C45fMh/EbCLI9LbCwAAgLOUI7pvrOs4gruXZ57X4pIuyl4GIODAF3PrAnbw3L2x4u6DaIdrhjEBAIAJuZPjF3kbPHZvrHhek8+tC4APBBxY8Xx2zfPDVFK4Lg6JLg4AADAB5WhupBlknl+aeV6Te97LYEQEHFiZWxewg8sBoy/w/IG0ac61sQAAYAIerQs4gufuDcn3mnxuXQB8IOCA+/kb8j3U6IuIXRxcGwsAAGqVc75SrI2v95dljXUBOzCHA5IIONCZWxewS0rJczvcJu8fTOtmitWyCQAAcIx76wKO4L17I8KafG5dAOwRcEDyfWatsS7gGEG7OGbWRQAAAPQp2LWwUpyXZI11ATt43tNgJAQckKQr6wJ2+MO6gBNE+YBaiXQ2FQAAYKfy8ibStbDuuzfWeF6be97TYCQEHBMX4LrQxrqAYwXs4piXM6oAAAA1uJfv+XKbIr0ca6wL2CXA3gYDI+DAr9YF7OH9rN82kT6oJOmegaMAACC6ssGN9OImUveG5H9t7n1vg4ERcGBuXcAOzymlpXURpyhdHA/WdRxhJgaOAgCA+KIdvQ31UqyszT2HHHPrAmCLgGPCyvlEz9cpNdYFnOm9pEgBDQNHAQBAWAEHiy6CdW+sNNYF7HDJenbaCDimzXv7nuchRnuVhPuDdR1HivbWAwAAYPXi7s66jiOF6t5Y432N7n2PgwERcEyb96uUGusCevCgWF0c85zztXURAAAAR4r2kmZRjjRH1FgXsIf3PQ4GRMAxUWWgpOd0M+z8jXXlf0O0dJ6BowAAIIzycmZuXMYxlpJurYs4VYA5HFesZaeLgGO6PIcbkv9k+GAppQdJrXUdR7hQvLcgAABggspG9t66jiN9qOBFXmNdwB7e9zoYCAHHdHm/Qul36wJ6Fi2lv+IecQAAEMCjupczUSwV66a9bbyv1b3vdTCQf1gXgPGVpPv/WdexS0qpuu/NnPNnxWqfbCX9XMEbBgAAUKHyMuazdR1HukkpLayL6EPO+f+sa9jj36xjp4cOjmny3rL1ZF3AQKLN4pgp3jRyAAAwAeWFXbQjtW0t4Ubhfc3ufc+DARBwTJP3li3vV0+dpNxzvjAu41hvOaoCAAAculP3MiaSG+sCeuZ9ze59z4MBEHBMTIDbUyT/afA53ivWtbGS9MgkagAA4EV5+fLWuo4jNeVlV028r9m5TWWCCDimx3u40Qa+E3yv8r/tg3UdR5qJoyoAAMCBoEdTpPq6N1br2ta4jH28733QMwKO6fHequU9Ce5DtGtjJY6qAAAAHyIeTXmo+AWe97W7970PekbAMSE555n8p5jer5w6W5nmHG3gqMRRFQAAYCjo0ZSo675DeV+7X5U9ECaCgGNavIcbywrPJr6oTNBujMs41kwcVQEAAAYCH025rfmq0rJ29/6/z/seCD0i4JiWN9YF7OG9xa1vt9YFnICjKgAAwMKj4h1Nea7sWthtvK/hve+B0CMCjonIOV/K/4eC9xa3XqWUntXN44iGoyoAAGA0OecrxXwLH/Fl1im8r+FnZS+ECSDgmI4IyWVjXYCBiNfGzhSzRRQAAAQT+GjKYipHrxVjDR9hL4QeEHBMQPlg8J56P9V8PnGb8r85Yrp/Vd6mAAAADOmTpGido1HXdycp61nvx1Su6ECeBgKOabiS/w8G761tgwk6cFTqjqrMrIsAAAB1yjm/lTS3ruME7yf44s77Wj7CC1/0gIBjGiK0ZHlPfYcWMeWP2jIKAACcKzMT7q3rOMFzSinijLVzRVjLR9gT4UwEHJUrN154H6ozyeMp6wIPHJ3nnN9ZFwEAAOoReO6GJN1YF2AhyDGVS24DrB8BR/1+sy7gAN5b2sbyXlJrXcQJ7phMDQAAenQn/y/oXvJQXlpNVYQ1fYS9Ec5AwFGxMh/h2riMfSKkvaMIPHBUkj4xuAkAAJyrDDF/a13HCVp1L6um7En+bwe8ZoZc3Qg46nZtXcABJn88ZV1K6UkxA5+Z4raSAgAAB8rGM+p64nbqa9ogx1SkGHsknIiAo1LlbXqEQToRWtnGdiv/6fdLrsq0cwAAgFNEvBJW6l7YRdjYjyHC2v4Nncf1IuCoV4SrYZd8GHwvpdQqbovjPfM4AADAsXLO94o5dyPyEePelbW99xd1XBlbMQKOet1ZF3CAhXUBXpXrxRrrOk7EPA4AAHCwwHM3JOl9eTmFrxbWBRwgwl4JJyDgqFDO+VrdTATvPloX4FzUtwEzxT0/CwAARhR87kZTXkrhWxHW+LOyZ0JlCDjqFCGRfJ74NVp7lf8+UY+qMI8DAAAcIurcjaWkG+siPCpr2Ajr/Ah7JhyJgKMygbo3PlgXEEFK6Z1ifEC85D7nPLcuAgAA+JRzflTMuRuS9IGjKTtFWOvTxVEhAo76RLg5RYpxhZQXkd8OMI8DAAB8p2wsr43LONVzeQmF7aKs9aPsnXAgAo6KlAFNEVLwxdTvCT9G8KMqF5I+WxcBAAD8KDeu3VvXcYbIL59GUdb6C+s6DnBZ9lCoBAFHXaJ8UEQYPORK8KMql+XqNwAAMHGlszPq3A2puzUl6ppsbFHW/KxTK0LAUYlAszfalFJjXURQkd8WvOWMIwAAUBduzKyLOBFHU45Q1vytcRmHYBZHRQg46hFlCnDUoxbmgh9VkbqhoxGOUAEAgAGUjs65dR0n4taU00RZu0bZS2EPAo4KBOreWCrOwCGXgh9VuZD0maGjAABMT1mvRr5CnqMpp3lStwfwji6OShBwBFc2i1HOjT0xXLQXN4rxQfESho4CADAxFQwVbVJKD9ZFRFTW/lFecN7zIi4+Ao743irOkKYoLWquVXBU5bLcew8AACpXNoyfFWe9uomjKeeLsm69UOwuI4iAI7Sc80xx7m5+Sim11kXUorxFaKzrOMN1zpkPEAAAKlZBuCFJN6xhz1P++0Xp4nhT9lgIioAjtjvF+cD4YF1AhV4r7lEVqWsD5N5xAADqdS8p8oDxp5RSlI25d1H2Ahdi4GhoBBxB5Zznkq6NyzjUM1fD9q+caYzeMvnIzSoAANQn5/xOcdaqL2kVf53lRtkLRBnSel32WgiIgCOuSIOaoiS24ZS3CgvrOs5wIekTA50AAKhHuY0i+lvwG4bj9y7SniDSXgtrCDgCKrMLorz1blNKC+siKnerOIn4S2bi+lgAAKpQOjOjDxN/T/dx/8qeoDUu41CXzIuLiYAjmLIJjJSIR0pqQ6rkqEr06+MAAJi8Em5Evw7+OaX0zrqIikXaG9zxAi4eAo54HhVnsOhSsY9PhFGujr21ruNM11wfCwBATGUjGGmd+pKluiHuGM5CcYbkr76nEQgBRyDlxolIt0584OzieMrVsdEnfV+Xc7sAACCItetgoxyh3oYrYQdW9gaRujiuuPUvFgKOIMoHR6QW/qWkB+siJuhGcVLxbR4JOQAACCX6dbCStOBK2NE8KNZ69Z6jKnEQcMRxp24YYxR0bxgo/81raK285/pYAAD8K8dLr63rOFMNR33DCNjFMVOsGYiTRsARQLmHOdIUX7o3DJWp3++t6zjThbqbVQg5AABwqtwycW1dx5mW4kpYC9G6ON6WPRmcI+Bwbm1gUyR0bxgr078b4zLOtQo5aAkEAMCZcpw00vHpbW7LsHaMKGAXh9Qdo2Zd6hwBh3+PinU0he4NP14rzl3j2xByAADgTAk3or2Ae8kipbSwLmLConVxzFTH933VCDgcC3hrikT3hhsVzeO4FCEHAAAulOOjNXRuPKeUbqyLmLKgXRzcquIcAYdTOeeZ4iWErejecKW0XNYwNOtS3fVzAADASAk3PqvrsIyslpdANXhQvI7jx7JXg0MEHH59UrwPj/d0b/iTUnqQtLCuoweXZVI7AAAYWUXhhtQNFW2ti8CXLo5ow/Ev1O3V4BABh0M554h3ibecYXTtVt0VaNFdE3IAADCuysKN9ymlJ+si8FXZQ7TGZRzrsuzZ4AwBhzPlTFekK2FXOMPoWEnHbxRrkNM2hBwAAIxk7Ua/GsKNptw0B38i7iXeMo/DHwIOR4LO3ZC6D4vGugjsVuZxRPzweAkhBwAAAyvhxmfF6yx+SSvmbrhV9hKNcRmnYB6HMwQcTpQPkIhzN6Q6hlhOQmnJjHbOcRtCDgAABlJZuLGU9JpZce5F3FNcSPrEbX9+EHD4EXHuhtTdH17DbIfJKK2ZtZw9vc45v7MuAgCAmlQWbkjSLetV/8rv0cK6jhPUcnVyFQg4HCgbtGvjMk6xVMykFd1RlVo+6O9yztfWRQAAUIMKw40HBuGHcquYM+N46eYEAYexsjG7s67jRFwLG1RlQ0el7vzjtXURAABEVmG48ZRS4mVcIEGvjV25Y+iovX9YFzBlwa/cek4p/WxdBM6Tc56r+x6sxQ1vaQAAOF6F4cazpFe8jIsp5/ynYn4vLtV939XSKR0OHRxGyrTdqOGGxNGUKpSJ1bXcrCLRyQEAwNEqDDeW6l56EG7EFXWvcSHpMzer2CHgMBD8xhSpGyzaWBeBfpSOh4VxGX16zDm/tS4CAIAIKgw3pO7GFN6gB1b2GgvjMk7FzSqGCDhGVsGHCINFK5RSulHMu8e3uecKWQAAdqtgXfqSG17EVSPqwFGp+5n6TMgxPgKO8UW9Dnbllna/ar1WPTerSN00a0IOAABeUGm4wY0pFSl7jsgvVrk+1gBDRkdUNlvX1nWcoUkpvbIuAsMJPvh2m0XpUAEAAKo23HhKKb22LgL9yzl/ljS3ruMMrEVHRAfHSCoIN1bXiqJi5bxqbSEWnRwAABTlZcbfqivceBbr1JrdKO5RFYm16AIhRXcAABu/SURBVKgIOEZQQbghSe9TSq11ERheCTlqWyTwwQIAmLxKOzVX13JG3gBjh7IHeW9dx5lYi46EIyoDqyTc4GjKBOWc30m6s66jZ89iEQQAmKDKw42aZohhiwqOqkgcVxkcHRwDqiTc4GjKRKWU3inu9VzbMNEaADA5Oecr1RduSFwHOzXRj6pIdHIMjoBjIJWEGxJHUyatJMxP1nX0jJADADAZOedrSZ9UX7jBdbATU8lRFYmQY1AEHAOoKNx4Sik9WBcBczeq6/pYqQs5/i7tugAAVKmEGzVupG65Dnaayt6khpdvhBwDIeDoWUXhBkdTIOnLHeSvVF/IcaGuk4OQAwBQnZzzveoMNxa8gJu8Go6qSIQcg2DIaE8qvE/8dUqphnQUPal0OJnUfUDyJggAUI2KXrhtYkAjJH2ZK/PJuo6eMAS/RwQcPagw3HhIKd1aFwF/Kg45pO4s78K6CAAATlXWpJ8U/6aJlzynlH62LgJ+lC6lt9Z19ISQoyccUTlT2fD9rXrCjWfCDWxTJpXXemXwI22CAICo1l64zY1LGULN6w+cqOxZajlCzXy4nhBwnKHCK7eWkl5bFwHfSshRa3vodc75kRtWAACRVPjCbR1vtrHLa9Uxj0P6Oh/uyrqQyAg4TpRzfqv6rty64UpYHKIc5ag25BDXyAIAgqjwhdu6pbq5cLVsYNGzsnepaU16IelT2WviBMzgOFLZ9NyrvsFNzN3A0Sq+fk6SWnWLqlpaHwEAlan8c3iprnODz2HsVdk8jpWFukH4BHxHIOA4Qs55pq5ro7b2P4Y24WQ553eS7qzrGMhSXWcTNwoBAFyp+KYUiXADJ8g5/6kK92nqXri11oVEwRGVA+Wc55Jq/KFZiqFNOENK6Z26hLlGqzbBa+tCAACQum7inPNnEW4Am16pnnkcK5eS/ix7URyAgOMA5Q11rWcbGdqEs5U76RfWdQyIG1YAAObWrmufG5cyJMINnKTsaWp8cbsaPvrOupAIOKKyQzmS8qh6P0RuyrBIoBeVt8tKTHIHABgpw0QfVecLtxXWpjhb5bNpGnExxE50cGxRPkT+VL3hxgMfIOjbBDo5Vm2CtR1VAwA4VuntfZsIN9CL8n30YF3HQObq1qJcJbsFHRwbKr4lZd1TSum1dRGo1wQ6OZbqplovrAsBANRrIutSiXADA8g5f5JUcxCwELesfIcOjjVrg0SvbSsZ1LPquisaDk2gk+NC3VyOe+tCAAB1Kkelax4mukK4gaHcqNv71OpaDCD9Dh0c+pKO36m+u5M3LSX9zJktjGUCnRxSdxbyNek5AKAvE5m3IRFuYGAlKPxT9f8sPUh6z3qUgGPVtfEoaWZbyeC4cgsmJhJy8PMFAOhFuSnhzrqOERBuYBRrtw/VHnK06n6uGuM6TE024ChdG4+q+1zWuldT/2aHnYmEHFJ3DrLWoVYAgAGVtekn1Tvgfh3hBkZVXmp/tq5jJE/qfsYm2c0xyYCjTKK+U/0p3gofIjA3oZBj0h8qAIDjlTfMn1R/R7HEuhRGKr8+dtNS3ZGVyb14m1TAUZK7e3VXPU4FHyJwY0IhR6tuLgdHVgAAO5UXb1MZWs26FKYmFnJI3ZDV2yl18k8i4CjDZe40jY3VuoeU0q11EcC6CYUck03OAQD7TfC4NOEGXCi34NV+ucSmhbp1aWtcx+CqDjjKB8dbSW80neMoK4tyVSfgzoRCDokjKwCADRM7krJU9zn4ZF0IsDKxtejKUtIHdS/Bq12XVhtwTHDOxjrCDbg3sQ+WVhxZAQBoUrekSNwyBscmthZdV3WXcXUBRzlXdadpJOIvIdxAGBNsEXyfUnpnXQQAYHwTuyVFItxAABMOOaTuBdz72o6OVRNwEGxIItxAQBMc9tSoa9VtjesAAIwk53yl7rNuKp3FhBsIY+Ihh1RZ0BE+4CDY+IJwA2FNMOTgPDIATEDp2rjTtLoVW3EsE8EQckiqJOgIGXCUD4trdcNDZ6bF+EC4gfAmGHJI3UTr25oHPQHAVE1skOjKs7rODT7XEA4hxxetumGki4g/y6ECjnLd67WmeSvKNoQbqEZZDH7WtH6+W3XdHI1xHQCAnkxskOgK4QbCI+T4xurWlUWko9UhAo6c81zSb+KbbRPhBqoz0ZBDkh7UtQWyMASAoMpn2KOkS+taRsaV6KgGIceLFpI+Rngh5zbgKMdQrtR1a0ztQ+IQN9HPRwHbTHiB+KzuZ5tzywAQTM75rbqujakF9LxwQ3UmenT6EM/qujqevAaa7gKOsrF5oy7cmNoHxKEIN1C9EnJ+1vRCDonrZAEgjHKE+lHTuf513UNK6da6CGAIhBw7LdV1bn3w9mLORcCxNjT0N01zM3OopbqBhAvrQoAxlGfDo7rAc2ro5gAA5ybctSHxwg0TUEKOe03zZ/xQz5I+yslQUrOAY+0Iyq+a5ublWNwnjsma+FlIZnMAgDOl4/he0+za4IUbJmXC8+FO8STpdxkeYRk94Mg5r4cafJMchje5mLyJTqRfacVNKwDgwsQ/j3jhhkma8Hy4U62OsPyeUnoa8xceJeAo3xC/qQs1ZmP8mhXhyi2g4Cwk3RwAYKXc6nev6W5wniW9jnRdJNCnic+HO0erLuz4OEY4OljAUQYuXYm5GudgKjWwgTZBLdV1c4yahgPAVJVNzZ2kt9a1GOKFG1BM/Oj0uVbzOp6GCkt7DTiYq9Gr25TSg3URgEe0CUqSGnVBR2tcBwBUqxytftR0Q3WJF27Ad8qA4XvrOoIbZF5HLwHH2lyN6z6+3sQt1bX/NdaFAJ7RJiipe1584EpZAOjXxK9+XccLN2CLcmztk6YdgPZloZ7mdZwccKzN1bgWv6l94WwjcCTaBCUxhBQAejPxIaIrHIcEDlDC0E+a9gu3Pi3VhR0nz+s4KuBYm6vxRgwL7dtCXUrO2UbgSLQJfvGk7jnSWhcCANGUjuR7scZt1b1w46YU4AClq/hevHDrWyvpg46c17E34Fibq/GbaNMbyg13iQPnoU3wC46tAMAROI7yDYaJAifitr9BNfo6nHTn82lrwFFS7NXVrhhGKxJyoDcMH/1Gq66bg/ZiAHhBeYn3VhxHWWGYKHCmshb9JDrBhrS6cvbFNe43AUf5DXmjLtSY+lvQoS3EkRSgd2XB+ijC2ZVG3bOGIBUAivKm9V6sd1foJgZ6wpGV0SzVhR0f1te5/yhtedfqujVmFpVNzFLdZmNhXQhQM4bEfWchQlUAE1eOM96LTr8Vbu8DBkKQOqpW3RGWxT9yzn+Kh/xYGnUJeWtcBzAJ5ajdo/hgWVmqG9b0QNABYErKC7170d23jnkbwMCY8TO6539IUs75Xt0ZRAznPUP/gPExl+NFrbpn0sK4DgAYFK3iWzFvAxgRncWjeEgp3X6ZwcGbzsE8q+va4Pw7YIS5HFu16p5PjXEdANCrtQGib8TadhPzNgADvHQbzFLdc+1J+n7I6Ezd1Ff+o/eDrg3AkZzzW3Vv8vCtRt3zqjGuAwDOVp71dyLY2NSK2/sAc3Rz9OpZ3XOtXf2NF6+J5cjK2ejaAJzijvKdGnHjCoCgyvP9TgzNf8mTurUp8zYAB+jm6MVDSul282++GHBIHFk50VLdW9AH60IAfI8OjoMt1D3LWuM6AGAvgo29mpTSK+siAHyPjrOTfHMkZdPWgEPiyMqRntS9+WytCwHwLSZYn2whgg4AThFsHKURN/kBLnHL01G+O5KyaWfAscKRlZ1adcHGiwkSAFt0o/ViIYIOAE4QbJxs51tPALbKmvVePNu2efFIyqaDAg6JTcIOtxxJAfzh5pRBLETQAcAIwUZvmMcBOMVx6hcdFc4eHHBIHFnZoRE3EABuEMgObiHpA8NIAYyBYGMQdHMAjuSc5+qec3PbStzZeyRl01EBxwpHVrZqRNABmKFrY3SNeOYBGEB5nl+JYGNodHMAhgg2djroSMqmkwIOiTekezRi0Q+MiinUphp1HR28CQRwlhJsvJX0RjzPx8ItgMDICDZ2OqvD7OSAQ+LIygEaEXQAg+KGFFdadc+8hXEdAIIpz/I7dV0bBBs2GnWz5Th+CAyEYGOvo4+kbDor4FjhyMpejQg6gF6tveW7s64F31lK+qCutZC2ZwBblcX+b5KubSvBmvfi+Q30imDjICcdSdnUS8AhcWTlQI1o4wbOVj4kHsW57AgWYiApgA1lcOhvYrHvVauum4M1K3CGskd+I551u/Q69Li3gEPiyMoRWtHGDRyN4yihNZI+8twDpmut8+43EVBH0ajbeLTGdQChcPvTwc4+krKp14BjhSMrB2vVtXEvaAMEtuM4SlVaSR9F+zMwGTnnS3VvMK+NS8HpHtS9nOO5DWxR1qvX6p53M9NiYujlSMqmQQIOiSMrR1qdV1+QkAPfKgn4vXiW1GihrqujMa4DQM/Wrnl9Izp7a8FtK8ALSofxtbj96VC9HknZNFjAIXFk5UQLcV4dYM7GtLSimw2owlq3Breh1KsV8zkAutNO0/uRlE2DBhwrHFk5SSMGkmKCmDI9eQvR1QGEQrfGZDXilkBMEINDTzbIkZRNowQcEkdWztCK8+qYAAaIYkOr7tnH0T3AqbUrXunWmLZGXUcH3ceoFkOSzzLokZRNowUcEkdWerAQx1dQmfJcuBPtfdiuURd2PBH0ArbWzpqzyMemhbqOjta4DqA3HEM52+BHUjaNGnCs5JzfidsQzvGs7rw6i32ERbCBEywlPUn6neN7wHg4goIjLUTQgcB45vXmfUrp3di/qEnAITFAsCerxT5dHQij/OyvBtABp1rq67wOnn9Az9YW+L+K5zVO04gZHQiEIcm9adUdSWksfnGzgEP68uF5L97g9qEVtxDAMYaHYkCturCXsAM4U5mZ9qtYm6E/jQg64FTZj16rCzZmpsXUYaFuJo/ZftQ04FhhAGnvVgt9WrhhLud8rS7YmNlWgoloRdgBHKws7uf62qnBWgxDadUFHQvjOoDV/nM1JBnnG3WQ6C4uAg7pywfso/gm6xNHWGCCSdNwotXXmR2NbSmAHxw/gbGlvnYdt8a1YEI4gjKYJ3XhhotTBG4CjpWSpt2LTVHfWvFWEwNbuzLw2rYS91p1t4K8ER+wY/kyoFRS4+VDGBhLGex8JekXEWrAj4W6tWljXAcqVUKNVafGzLaa6rTqjqOYd22scxdwSF/eLNypewOM/rUi7EBPmDR9tEbddVlL5hCZepL0h7rbqFrjWoBBlND5V3VHUHg+j2sp6VbdxmpuW0oIrZglh54QaoziQd2RM3c/ry4DjhVuWhlFK8IOnGCtW4M2v8O9eF1W+W95LzYgVlqVwMPbWwjgGKVLY66voQbPZhvfLPxzzu/UvbjDYRaiqwNHItQYTSvDG1IO4TrgWOGDYTStOK+OHcri+VrM1jjWQYOXykDWe7Epsbbq7mgIfuFdOdr7i+jS8KBR16793XODgfonafV1llxrWwo8WutSI9QYx4sv6rwJEXBIXzZWj6LNbyyr8+qrFm537UcYx9q57d/E4vkUz+qOpLSH/MNrA1oJdX1YfxY2LLJhrSzo5/oaasBeqwPOoZfP00/is/QUz+pmV3GscMLWjkWvZgkRGI6jUfeirjWu4yBhAo4V3nCaafR1OB9vNCu3Fmqs2pxxmoeU0u0p/2L5PbgXwwC9adU9D/+Q9MzzEEMj0HBtqa674N0x/1LO+V7MmTsHYceElKMnc7EmtbBUF94urAs5RriAQ2IIqQO80awQnRq96u0ucOZzuLfUt4FHY1oNQivrm7m6MGO1qIdPC3UL/5M6XDmy0ptV2MELuEqszRKiS8OW2yGi+4QMOFZKoncvFgDWWn3b4RHuB2Gq1s5uc3axP0cdSTlU6V67E79PETTqvg/+EiEwdigB5qWkn9StZWaG5eAwjXpq1ebISu9aMTA6nLVgd9WhMTMsBztmCUUROuBYKZu0e/ED4UWrr280Wdw7stbmtwo10K/Bhy+VoctvxBuNSJbqAo8/yv995rk4PSXMmOlrmMGmNpZG3TO+6fsLM0x/MAyMdmijQ2Mu9m9etOqecQvjOs5WRcAhfTOYj4W/P6sW7tXbzMa0mgnZCDTm4mdjKK1GvDKLQaRVWIUeq06PlmdjHcrP5+rZ+5/y54QZcbUaYdFfArBHsdkbyvpxQgKPEa3NEVqFu6xFfVlK+qBublwVXfjVBBwrJRW8U3eVJfxaX9hzbr0HG4vq1fltPkSG96Qu3Bj9Q4HnXZWe1W2o/ip/vuT56NPaM/dS3wYZPHfr0Grkt5nle+pRdFiOYb2zrlG3Fq1ic2dp49gd4a5/C3XPuda4jl5VF3CslB+wOzGfI5JWa6GHujeaJOwv2AgzVh8iM8OSpqi3QaLnIuiYhFZfn5H/U7cgX/KMHF5ZT6yeuf9R96yd21WEgZm/zWQAqZlWa/OT1K1DW8N63CodwjN9G2bMDEvCcRoNdOTOg2oDjhXmc1Rh/Y1mW/6YRNJeNq4z8YbQm0bdIFFX34Pl++VRbL6mZvUmspX037W/JgDZYy0slr7+3Pyy8deYBvNgY1353vwkvg+trR8n/K++voBrLYsaw9rzcaav84NmoisjslbdAFHzl3NDqj7gWMk5r86rszGsy7O+fvj8b+2vQyzsdyyu1/8+/FiqS7wfrAvZhQ42vODLs1FdWCx9DYylyhbs5e3i6vN+/c9X4cVMvPhAx1WwsYn1q2ur5+of5a+b1d/3+L20ae05uVpz/qCvz0vWoHUJsX7ty2QCDolBpBO2+gCSvr7hfOn/98UxLVsbIcW6mb5dQP+klxfciKFRT1cDjoWgA2doNv76jxf+mRefny94MTwp35+HmOn7MGK1EF9hQY5juQ421tGdF1pT/u96uCx9GzCvOzhs3rH+3Pz7q6N1EuHu1IR5zvVpUgHHSnkg3Ivz6gD2czNr41QEHQDwRdgFf875Wt36lRckAPZZqDuOEuo514dJBhwrDOYDsMeTjG5IGUIJOn4TzzwA0xM22FjHTSsA9liowptRjjHpgGOFoAPAhlZdsNEY1zEInnkAJqSV9FHBg41NJbB+FMcNAHQWmniwsULAsYZFPwBJ71XZQngbnnkAKtaqW+wvjOsYzNpsuTvrWgCYWYhg4xsEHC9g0Q9MUqPurKL723f6xgBmABVpJH2sOdjYVG7DuBdzloApWYhg40UEHDsQdACT0Kryt3yHKkHHlbrn3sy2GgA4ypOkD7UeLTwEQ0iBSViIYGMnAo4DEHQA1ZrMcZRjlYXyb+KNIADfFmKx/wXHVoBqLcSz7iAEHEcg6AD+f3t3c922EYUB9OsgJVgd2B2YHUQdxB24hlSQEsISlA7oNTdMB5MOlA6yAGCMZFGxKJCDn3vPwaETbWY1eOfDezOr8ZBuHKW0XsjcuXkFmKGSFR4cOqW+Zv0zQmpYun0EG28i4LiAeXVYrFO6YOPQeiFL0xfLX2LfA9o5ZGPna7xXH1L/keRT46UAP28V11q3IuB4hz7o+JKu4L9ruhjgNSXO2ZhMP77yNQpm4Db26c7X2Nwh0FPp923nK8G8lXTBxl6wcTkBx0QU/DBLEvAr6k/u/xrjK8D0ShT6k9KBDLN1Shfi7lsvZA0EHBPrWwG/pruJAGhDsHFDutmACT2kG0N5aL2QtRJ0wGxs/vanaxBwXEk/rz582fTygNvZx2FMzTiUFLhASXdo6N7efTsOz4cmHjOO3ZW2S1knAceV9Sn5fcw9wrXtI9iYjWrvM7oHnLNP161xaLyOTRN0wE2UJL8nedBdfF0CjhsyvgJXsY9gY9b6szqGrg4dbbBtp3QjhIr8mRF0wFUYQ7kxAUcD1XWLv0VXB1ziMd0LQ7CxMMfj8T7d3ifohe0oGYv80nYp/B9BB7xbibG7ZgQcjSn24U0cHroSRlhg9YYg+i8Hhi6Tw0jhzRySPAMCjpnQ1QGvKnFd4Gr1+98Q9go7YNmGUGPfeiFMw01Z8KoS3RqzIuCYoeoWgvtIzNk294JvTHVex30U0rAUD0n+inM1Vu94PH6JzjsYOtQckjxDAo4Zq1q4f0uya7sauKl9vDQ2T9gBsybU2DBXgrNRh3TdGva9GRNwLETVwq09kLUq0eLHGVXYsYsvh9CKUIMnqhFr53SwViXjzU+l7VL4GQKOBfJVk5U5pOvW2DdeBwvhzA64maEN+1uEGvyPfnxF1zFrUDKOoJwar4U3EnAsnLCDhSpxZSATqEb5fo3bqGAKJX2o4SYALtGH0F+jNmVZSoQaqyDgWBFhBwvg+iyuqr96+3Psg/AWh3SjJweFPVPq9+ShNoW5KRFqrI6AY6WEHczIKePsovZmbqYaZRkCD6BT8jTUsDdzVVW3nRtYaK1EqLFqAo4NMK9OA6eMp0yXxmuBJN9P/f81Diplex7TBRrDWRql6WrYNHUpDahLN0TAsTHP5tV3ceI10/HyYDH6vXAX4yys1yFdoHFw5TZzVZ3XsYuwg+k4IHnDBBwb13/R3KULPLxYeCuhBqvQF9m7dIHHp9gPWZ5DBBosmM4O3umUcezu0HgtNCTg4Luqu+NzukL/ruV6mK2HjC+Q0ngtcBXPOjw+xbWHzEs9cnJSzLM2VejshizOKXk6eqdLgyQCDl7x7IvmLgKPrSpxZSDUHW8f04Uedw2Xw7ac0hXyf0e4zAa5IYs8DTTsg5wl4OCnCTw2o55b9AKBM/o9cRhnGTo9nGvEe5V0gYbuDHjBs3r0PvbdtSoRaHABAQcX08K9GiVjMX1wZRZcTujBG5365590hfxJmzW8zfF4HGrQYc+9a7keLnbI03rUXshFBBxMqnrJDC3cDomaH63OcEN9GDzsjR/SFd+7diuigceMYcbfSYrODLiOqsPjY9zOMlf1fujjGpMScHB1/dz6p3SFvU6P26pfIFqdYUb6QHjohBN8rEPJ2BX3T/+rKwMaq2pRH+Bu75BqT1SLcm0CDpro0/W7KOynUn8d9AKBBas6Pu7652PGIIT2SvUMIcajPReW5YUPcEYK3+eQcV88pOtUK+2Ww1YJOJiVKvgYXjKf+19Je+eULsz4lirU8HUQtqMvypMx8Pjc/yrOp1H65zFd99uw1z5qo4Z1qwJmdejLXqpDBRnMioCDxaheOvWLZijs77KOQ6UO/W9Jl4CX4fHyAH5GFRQnYwjyofp/9d+3ZCjMk644T8YCPREWA6+o9tbhqffV3e1XNLnSP8m4Rw77pv2RxRBwsDrVXHvy9IvmL/kxgb/LdQr9kvElMfh25u/CC6CJZ2FI8mMXyIe8vEfurrWmM+ogonZK8m/13yXV3mtsBLilZ3tq/e9k/CiXM3+fSsmPNWi9V9b7qc40VkfAAQBcRTVOc46AFwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADe6T9j+ggBdrNYQAAAAABJRU5ErkJggg==" - }, - "asset-e79711e8-d9da-45e1-a234-9efe226a444d": { - "id": "asset-e79711e8-d9da-45e1-a234-9efe226a444d", - "@created": "2018-09-06T20:01:04.447Z", - "type": "dataurl", - "value": "data:image/png;base64,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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQJ7/B75mKmIfIcbtAAAAAElFTkSuQmCC" - }, - "asset-9493e336-1b11-4e61-bad2-716c46194550": { - "id": "asset-9493e336-1b11-4e61-bad2-716c46194550", - "@created": "2018-09-06T20:01:22.463Z", - "type": "dataurl", - "value": "data:image/png;base64,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" - }, - "asset-23f2bfe9-e58c-4a56-98c6-fad59eecdf74": { - "id": "asset-23f2bfe9-e58c-4a56-98c6-fad59eecdf74", - "@created": "2018-09-06T20:01:50.445Z", - "type": "dataurl", - "value": "data:image/png;base64,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" - }, - "asset-86d05b5e-1a4b-4979-95e9-7071b9923470": { - "id": "asset-86d05b5e-1a4b-4979-95e9-7071b9923470", - "@created": "2018-09-06T20:17:48.355Z", - "type": "dataurl", - "value": "data:image/png;base64,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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH9/8BS8cNlOF3xjoAAAAASUVORK5CYII=" - }, - "asset-fdfc9cc7-2c6a-44fe-b9be-c4ff115c92c1": { - "id": "asset-fdfc9cc7-2c6a-44fe-b9be-c4ff115c92c1", - "@created": "2018-09-06T20:18:11.818Z", - "type": "dataurl", - "value": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABDgAAAQ4CAYAAADsEGyPAAAACXBIWXMAAAsSAAALEgHS3X78AAAgAElEQVR4nOzdzY0sSXYm0MsBBXBKMFGA7ydbAmZLMNlb31S0BNUlQXVJ0PUkoNfGt8yRoIMSTM4+gI6RgD4azCKtm6+q3k/+WMR1Mz8HaBAgCPJbMF9c+8LiWgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/5p+wAwLas03gXEXcRcYiI/17+KwDAFlwi4v+W//o0LOen1DTApig4YOdKofEQEf8aEfe5aQAAXu0UEf8REY8KD9g3BQfsUCk1vo2IY0QMuWkAAKpZI2KOiJ+VHbA/Cg7YkXUajxHxXTz/BAUAoGdPEfFhWM5zdhDgNhQcsAOl2Pgh7NMAAPbnEhE/KjqgfwoO6Ng6jfcR8ZdwYwMA4Ckivh+W8yk7CHAdCg7o0DqNQzwXG8fkKAAAWzPHc9GxZgcB6lJwQGfKrY1/Cz9HAQD4nEtE/NFtDujLf8sOANSzTuOfIuKvodwAAPiSQ0T8tcxOQCfc4IBOrNP4b+EnKQAArzUPy/mP2SGA91NwQOPKvo2/hkWiAABv9RQRv7eXA9qm4ICGKTcAAKpRckDjFBzQKOUGAEB1Sg5omCWj0K5/C+UGAEBNd/E8YwENUnBAg9Zp/EtEPGTnAADo0EOZtYDG+IkKNGadxmP4ZgEA4Nr+OCznOTsE8HIKDmjIOo2HiPjfETEkRwEA6N0aEb8blvMlOwjwMn6iAm35t1BuAADcwhBuzUJTFBzQiHUa/xQR99k5AAB25L7MYEAD/EQFGlCehP1buL0BAHBra0R84+lY2D43OKANfwnlBgBAhiGeZzFg49zggI0ri0X/lp0DAGDnvrFwFLbNDQ7Yvh+yAwAAYCaDrXODAzas7N74z+wcAABERMS/2MUB2+UGB2ybrd0AANthNoMNU3DAtn2bHQAAgH8wm8GGKThgo9ZpvIuIQ3YOAAD+4VBmNGCDFBywXb4hAADYHjMabJSCA7brITsAAAC/YUaDjVJwwAat03gIP08BANiiQ5nVgI1RcMA23WcHAADgs+6zAwC/peCAbfof2QEAAPgssxpskIIDtsl2bgCA7TKrwQYpOGCbfGgCAGyXWQ02SMEB2zRkBwAA4LPMarBBCg7YmHUafSMAALBxZjbYHgUHbI9vBAAAts/MBhuj4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmvfP2QEAKnmKiO+zQwBAg/4SEXfZIQDeS8EB9GIdlvMpOwQAtGadxjU7A0ANfqICAAAANE/BAQAAADRPwQEAAAA0T8EBAAAANE/BAQAAADRPwQEAAAA0T8EBAAAANE/BAQAAADRPwQEAAAA0T8EBAAAANE/BAQAAADRPwQEAAAA0T8EBAAAANE/BAQAAADRPwQEAAAA0T8EBAAAANE/BAQAAADRPwQEAAAA0T8EBAAAANE/BAQAAADRPwQEAAAA0T8EBAAAANE/BAQAAADRPwQEAAAA0T8EBAAAANE/BAQAAADRPwQEAAAA0T8EBAAAANE/BAQAAADRPwQEAAAA0T8EBAAAANE/BAQAAADRPwQEAAAA0T8EBAAAANE/BAQAAADRPwQEAAAA0T8EBAAAANE/BAQAAADRPwQEAAAA0T8EBAAAANE/BAQAAADRPwQEAAAA0T8EBAAAANE/BAQAAADRPwQEAAAA0T8EBAAAANE/BAQAAADRPwQEAAAA0T8EBAAAANE/BAQAAADRPwQEAAAA0T8EBAAAANE/BAQAAADRPwQEAAAA0T8EBAAAANE/BAQAAADRPwQEAAAA0T8EBAAAANE/BAQAAADTvn7MDAFRyWKfxz9khAKBBh+wAADUoOIBeHCLih+wQAABADj9RAQAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfgAAAAAJqn4AAAAACap+AAAAAAmqfggO35n9kBAAD4KjMbbMw/ZQcA/ss6jUNE/C0ihuwsAAB80RoR3wzLec0OAjxzgwO25SGUGwAALRjieXYDNkLBAdvyXXYAAABezOwGG6LggI1Yp/EuIu6ycwAA8GJ36zTeZ4cAnik4YDt8AwAA0J5vswMAzywZhQ0oy0X/MzsHAABv8i+WjUI+NzhgG47ZAQAAeLNjdgBAwQFb4ecpAADtMsvBBig4INk6jQ8RccjOAQDAmx3KTAckUnBAPoupAADaZ6aDZJaMQqJ1Gg8R8bfsHAAAVPHNsJwv2SFgr9zggFzH7AAAAFRzzA4Ae6bggFwWUgEA9MNsB4kUHJBkncZjRAzZOQAAqGYoMx6QQMEBeSyiAgDojxkPklgyCgnWabyLiP+dnQMAgKv43bCcn7JDwN64wQE5/D4TAKBfZj1I4AYH3Ng6jUM8Pw1r/wYAQJ/WeH4yds0OAnviBgfc3kMoNwAAejbE88wH3JCCA27vh+wAAABcnZkPbkzBATe0TuN9RBySYwAAcH2HMvsBN6LggNvybBgAwH6Y/eCGLBmFGynLRf8zOwcAADf1zbCcL9khYA/c4IDb+VN2AAAAbu6YHQD2QsEBt+OKIgDA/pgB4UYUHHAD6zQ+hOWiAAB7dCizIHBlCg64je+yAwAAkMYsCDdgyShc2TqNh4j4W3YOAABSWTYKV+YGB1yfxh4AADMhXJmCA67vmB0AAIB0x+wA0DsFB1zROo3HiBiycwAAkG4osyFwJQoOuC5XEQEA+DuzIVyRggOuZJ3Gu4i4y84BAMBm3JUZEbgCBQdcj4YeAIBfMyPClXgmFq5gncYhIv4zOwcAAJv0L8NyXrNDQG/c4IDrOGYHAABgs47ZAaBH/5wdADrl6mFdl/Ifbusu2n0F6CkifDP2OofynxZdwr8RrzVEu3ui1nj+G+e2DtHuvxFb9F1E/JQdAnrjJypQ2TqN9xHx1+wcnfnDsJwfs0PszTqNf42I++wcb/T7YTmfskO0ZJ3GP0fED8kx3urHYTn/OTtESxr/rDoNy/n32SH2Zp3Gh4j49+wcnfFZBZX5iQrU5/ZGXRflBgDkKp/Fl+wcnTEzQmUKDqhoncZDRDxk5+jMz9kBAICI8Jlc20OZHYFKFBxQ1zE7QIfm7AAAQETYGXENx+wA0BMFB9T1bXaAzszDcr5khwAAIsqzpnN2js74mQpUpOCAStZpPIbt4rW5CgsA2+Kzua6hzJBABQoOqMftjbouNosDwLaUz+ZLcozemCGhEgUHVFAWRN0nx+jNj9kBAIBP8hld171lo1CHggPq+CE7QGfWiPA0LABs02M8f1ZTj1kSKlBwwDut0ziEp2FreyyLzACAjSmf0b6IqOuhzJTAOyg44P0eIsIHUl0fsgMAAF/ks7ouX5hBBQoOeD/Pe9V1GpbzU3YIAODzymf1KTtHZ8yU8E4KDniHdRrvI+IuO0dnPD8HAG3wmV3XXZktgTdScMD7eNarrnVYznN2CADg68pntp1ZdZkt4R0UHPBGZRHUMTtHZ/yeFwDa4rO7rqNlo/B2Cg54uz9lB+jQnB0AAHiVOTtAh8yY8EYKDng7VwjrehyW8yU7BADwcuWz25OxdZkx4Y0UHPAG6zQ+RMQhO0dnLCoDgDb5DK/rUGZN4JUUHPA2mvW6LsNy9u0PADSofIZfsnN0xqwJb6DggFdap/EQEVr1uiwoA4C2+Syv66HMnMArKDjg9b7LDtChOTsAAPAuc3aADpk54ZUUHPB6x+wAnZmH5bxmhwAA3q58ls/ZOTpzzA4ArVFwwCus03iMCG+T12UxGQD0wc9U6hrK7Am8kIIDXsfCp7qehuV8yg4BALzfsJyfIuIpO0dn/EwFXkHBAS+0TuNdRNxn5+iMb3oAoC8+2+u6KzMo8AIKDng5DXpda0R4GhYA+vIYz5/x1GMGhRdScMALrNM4hKdha7NcFAA6Y9noVTyUWRT4CgUHvMwxLBetzRVWAOiTz/i6hvCiCryIggNextXAuk7Dcr5khwAA6iuf8afkGL0xi8ILKDjgK9ZpvI+IQ3KM3ngaFgD65rO+rkOZSYEvUHDA13katq7LsJzn7BAAwPWUz/pLcozemEnhKxQc8AXrNB7Cbx5r840OAOyDz/y6jmU2BT5DwQFfdswO0KE5OwAAcBNzdoAOHbMDwJYpOODLXAWsa7ZcFAD2oXzmz8kxemM2hS9QcMBnrNN4DMtFa3NVFQD2xWd/XYcyowKfoOCAz9OQ13UZlvMpOwQAcDvls/+SHKM3ZlT4DAUHfEJZ4HSfHKM3H7IDAAApzAB13Vs2Cp+m4IBP+y47QGfW8BtcANirOZ5nAeoxq8InKDjgV9ZpHMKG6toeh+VssAGAHSozwGN2js4cy8wKfETBAb/1EBE+MOpyNRUA9s0sUNcQzzMr8BEFB/yWK391PQ3L+Sk7BACQp8wCp+wcnTGzwq8oOOAj6zTeR8Rddo7O+MYGAIjwZGxtd2V2BQoFB/ySZ7fqWoflPGeHAADylZnATq66zK7wEQUHFJaLXsWcHQAA2JQ5O0BnLBuFjyg44L8cswN0yM9TAICPmQ3qO2YHgK1QcMB/saiprsdhOV+yQwAA21FmA0/G1mWGhULBARGxTuNDRByyc3TGIjEA4FPMCHUdyiwLu6fggGcWNNV1GZazb2cAgN8oM8IlO0dnzLIQCg6IdRoPEaH1rss3MwDAl5gV6nooMy3smoIDLGa6hp+yAwAAm2ZWqO+YHQCyKTjAYqba5mE5e+MeAPisMivM2Tk6Y6Zl9xQc7No6jceI8HZ4Xa6cAgAvYWaoayizLeyWgoO9s5CprqdhOZ+yQwAA21dmhqfsHJ0x27JrCg52a53Gu4i4z87RmQ/ZAQCAppgd6rovMy7skoKDPfM7xbrWiPA0LADwGo/xPENQjxmX3VJwsEvrNA7hadjaHi0XBQBeo8wOviCp66HMurA7Cg726iEsF63tx+wAAECTzBB1+SKP3VJwsFc/ZAfozGlYzpfsEABAe8oMcUqO0RuzLruk4GB31mm8j4hDcozeeOYNAHgPs0RdhzLzwq4oONgjz2fVtQ7Lec4OAQC0q8wSl+QYvTHzsjsKDnZlncZDRByTY/TG824AQA1ucdR1LLMv7IaCg705Zgfo0JwdAADowpwdoEPH7ABwSwoO9sZVvboeLRcFAGooM4UnY+sy+7IrCg52Y53Gh7BctDY/TwEAajJb1HUoMzDsgoKDPfkuO0BnLsNyPmWHAAD6UWaLS3KM3piB2Q0FB7tQFizdJ8fojW9YAIBrMGPUdW/ZKHuh4GAvNNf1zdkBAIAuzdkBOmQWZhcUHOzFMTtAZ+ZhOa/ZIQCA/pQZY87O0ZljdgC4BQUH3Vun8RgRQ3aOzrg6CgBck1mjrqHMxNA1BQd74EpeXU/Dcn7KDgEA9KvMGuaNuszEdE/BQdfWabyLiLvsHJ3xjQoAcAtmjrruymwM3VJw0DtNdV3rsJzn7BAAQP/KzGHnV11mY7qm4KBb6zQOYaFSbXN2AABgV+bsAJ05lhkZuqTgoGfH7AAdclUUALgls0d9x+wAcC0KDnrmCl5dp2E5X7JDAAD7UWaPU3KM3piR6ZaCgy6t03gfEYfkGL3xDQoAkMEMUtehzMrQHQUHvdJM13UZlvNjdggAYH/KDHLJztEZszJdUnDQnXUaDxHxkJ2jMz9nBwAAds0sUtdDmZmhKwoOenTMDtChOTsAALBrP2UH6NAxOwDUpuCgR67c1TVbLgoAZBqW8xq+cKnNzEx3FBx0ZZ3GY0R427suV0IBgC0wk9Q1lNkZuqHgoDffZgfozGVYzqfsEAAAZSa5JMfojdmZrig46EZZlHSfHKM3P2YHAAD4iNmkrnvLRumJgoOe/JAdoDNrRHgaFgDYksd4nlGoxwxNNxQcdGGdxiE8DVvbY1noBQCwCWU28QVMXQ9llobmKTjoxUNYLlrbh+wAAACfYEapyxeFdEPBQS88c1XXaVjOT9khAAB+rcwop+wcnTFL0wUFB81bp/E+Iu6yc3TGM2wAwJaZVeq6KzM1NE3BQQ88b1XXOiznOTsEAMDnlFnFrrC6zNQ0T8FB08pCpGN2js74XSsA0AIzS11Hy0ZpnYKD1v0pO0CH5uwAAAAvMGcH6JDZmqYpOGidq3R1PQ7L+ZIdAgDga8rM4snYuszWNE3BQbPWaXyIiEN2js5Y2AUAtMTsUtehzNjQJAUHLdMw13UZlrNvQQCAZpTZ5ZKdozNmbJql4KBJ6zQeIkK7XJdFXQBAi8wwdT2UWRuao+CgVd9lB+jQnB0AAOAN5uwAHTJr0yQFB606ZgfozDwsZ2/JAwDNKTPMnJ2jM8fsAPAWCg6as07jMSK80V2Xq50AQMvMMnUNZeaGpig4aJErc3U9Dcv5KTsEAMBblVnGPFOXmZvmKDhoyjqNdxFxl52jM77xoEeX7ACwcZfsAHAFZpq67srsDc1QcNAaTXJda0R4GpbuDMv5kp0BtszfCJ16jOfZhnrM3jRFwUEz1mkcwtOwtVkuype46gtskX+b+CTLRq/ioczg0AQFBy05huWitbnKyZf8v+wAb3TKDgCNOGUHeKNW/23iNsw2dQ3hRRUaouCgJa7I1XVyRZmvOGUHeKNLdgBoRKs3IU7ZAdiuMtuckmP0xgxOMxQcNGGdxvuIOCTH6M3P2QHYtmE5n7IzvNF/ZAeARvyf7ABv0fC/TdyOGaeuQ5nFYfMUHLTi2+wAnbkMy3nODkETWlxC22JmyNDi30qLmbmxMuNckmP0xixOExQcbN46jYfw27/afLPBS/2v7ACv9GhxLrxM+VtprTBo7d8k8ph16jqWmRw2TcFBC47ZATo0ZwegGa09uWeghddp6W+mxUKGPHN2gA4dswPA1yg4aIErcXXNlovyUuUb3lY20l+G5ezwA69Q/mYu2Tle6IMbWrxUmXXm5Bi9MZOzeQoONm2dxmNYLlpbS9/WsQ0/RRu3OH7MDgCNauFvZ43nf4vgNcw8dR3KbA6bpeBg6zTFdV1sn+e1yjemWz8AnSzOhbcpfzun5Bhf86PbG7xWmXkuyTF6YzZn0xQcbFZZZHSfHKM3rfzUgI0ZlvNPEfGUneMLvs8OAI3b8t/QU/k3CN7C7FPXvWWjbJmCgy37LjtAZ9bwW1Te5w+xzZ+qfD8s5y2XL7B55W9oiyXHGs//9sBbzbHNz66WmdHZLAUHm7RO4xA2Ndfm+UzepSxs+2N2jl+ZfbMLdZS/pTk7x6/80WJs3qPR55C37lhmddgcBQdb9RAR/uGsyxVN3q28uLCVkmMelvNWskAXyt/UnJ2j+KOXkajEDFTXEM+zOmyOgoOtcvWtrpMr/NRSFhJmFwvKDbiSjZQcf7Q4mFrKDHTKztEZszqbpOBgc9ZpvI+Iu+wcnfFMGlWVg0fWTo6flBtwXeVvLOPnX2tE/EG5wRWYheq6KzM7bIqCgy3y/FRdq0GRayhXx38Xt/tW7O8Hny0uQoTulL+1WxaZTxHxOz9L4RrKLGQXWV1mdjZHwcGmWC56FXN2APo1LOfLsJx/H8+vL1xzcJwj4hsHH7it8jf3TVz3s2SN59eQfmehKFc2ZwfojGWjbI6Cg605ZgfokMVaXF15feGbiPgx6hYdczwXG3/0ChDkGJbzWn6yUrvoWOP534xvvIbEjZiJ6jtmB4CP/VN2APjYOo1/i4hDdo6OPA7L+Q/ZIdifdRqPEfE/421b1p/i+bfSj77NvZ11Gv8cET8kx3irH4fl/OfsEHuxTuMhnv+2v4237cx6jIj/5eeTZFin8d/DCyA1XYbl/E12CPi7f84OAH+3TuNDKDdqs1CLFOXgMkf8YnHwEBH/+on/8UtE/N943uXx5KYGbFspHn+KiJ/K9fS7iLiPiP8en/4c/494vq3xNCzn001Cwuf9HAqOmg7rND74CSlboeBgSywqquviw4YtKAeaU3IM4ApKIXkKf+M0YljOj+s0XsKXajV9G883syCdHRxswkfXXanH7Q0AgN8yI9X1UGZ5SKfgYCuO2QE6ZGEbAMBvmZHqO2YHgAgFB9vxXXaAzsz2GAAA/FaZkebsHJ0xy7MJCg7SldcWvKFdl6uXAACfZ1aqaygzPaRScLAFlovWZUs9AMAXlFnpKTtHZ8z0pFNwkGqdxr8/LUc9H7IDAAA0wMxU132Z7SGNgoNsfq9X1xqe6QIAeInHeJ6dqMdsTyoFB2nWaRzC07C1PVouCgDwdWVm8sVQXQ9lxocUCg4yPYTlorX9mB0AAKAhZqe6fIFJKgUHmX7IDtCZ07CcL9khAABaUWanU3KM3pjxSaPgIMU6jfcRcUiO0RvPnQEAvJ4Zqq5DmfXh5hQcZPGMVF2XYTnP2SEAAFpTZqhLcozemPVJoeDg5tZpPETEMTlGb3zzAADwdmapuo5l5oebUnCQ4ZgdoENzdgAAgIbN2QE6dMwOwP4oOMjgylpdj5aLAgC8XZmlPBlbl5mfm1NwcFPrND6E5aK1fcgOAADQATNVXYcy+8PNKDi4te+yA3TmMiznU3YIAIDWlZnqkhyjN2Z/bkrBwc2URUP3yTF645sGAIB6zFZ13Vs2yi0pOLglDW59c3YAAICOzNkBOuQMwM0oOLilY3aAzszDcl6zQwAA9KLMVnN2js4cswOwHwoObmKdxmNEDNk5OuMKJQBAfWasuoZyFoCrU3BwK66m1fU0LOen7BAAAL0pM5Y5qy5nAW5CwcHVrdN4FxF32Tk645sFAIDrMWvVdVfOBHBVCg5uQWNb1zos5zk7BABAr8qsZddZXc4EXJ2Cg6tap3EIi4Vqm7MDAADswJwdoDPHcjaAq1FwcG3H7AAdcmUSAOD6zFz1HbMD0DcFB9fmKlpdp2E5X7JDAAD0rsxcp+QYvXE24KoUHFzNOo33EXFIjtEb3yQAANyO2auuQzkjwFUoOLgmDW1dl2E5P2aHAADYizJ7XbJzdMYZgatRcHAV6zQeIuIhO0dnfs4OAACwQ2awuh7KWQGqU3BwLcfsAB36KTsAAMAOmcHqO2YHoE8KDq7F1bO65mE5e4sdAODGygw2Z+fojLMCV6HgoLp1Go8R4Y3rulyNBADIYxarayhnBqhKwcE1fJsdoDOXYTmfskMAAOxVmcUuyTF648xAdQoOqioLg+6TY/Tmx+wAAACYySq7t2yU2hQc1PZDdoDOrBHhaVgAgHyP8TybUY+zA1UpOKhmncYhPA1b26PlogAA+cpM5ounuh7KGQKqUHBQ00NYLlrbh+wAAAD8g9msLl+QUpWCg5o891TXaVjOT9khAAB4VmazU3aOzjhDUI2CgyrWabyPiLvsHJ3xHBkAwPaY0eq6K2cJeDcFB7V45qmudVjOc3YIAAB+qcxodqTV5SxBFQoO3q0sBjpm5+iM33cCAGyXWa2uo2Wj1KDgoIY/ZQfo0JwdAACAz5qzA3TImYJ3U3BQgytldT0Oy/mSHQIAgE8rs5onY+typuDdFBy8yzqNDxFxyM7RGYurAAC2z8xW16GcLeDNFBy8l6a1rsuwnH0bAACwcWVmu2Tn6IyzBe+i4ODN1mk8RISWtS4LqwAA2mF2q+uhnDHgTRQcvMd32QE6NGcHAADgxebsAB1yxuDNFBy8xzE7QGfmYTl7Ux0AoBFldpuzc3TmmB2Adik4eJN1Go8R4a3qulxxBABojxmurqGcNeDVFBy8latjdT0Ny/kpOwQAAK9TZjhzXF3OGryJgoNXW6fxLiLusnN0RvMPANAus1xdd+XMAa+i4OAtNKp1rRHhaVgAgHY9xvNMRz3OHLyagoNXWadxCE/D1ma5KABAwywbvYqHcvaAF1Nw8FrHsFy0NlcaAQDaZ9VlJ3MAACAASURBVKarawgvqvBKCg5ey1Wxuk7Dcr5khwAA4H3KTHdKjtEbZw9eRcHBi63TeB8Rh+QYvfk5OwAAANWY7eo6lDMIvIiCg9f4NjtAZy7Dcp6zQwAAUEeZ7S7JMXrjDMKLKTh4kXUaD+E3cLVp+AEA+mPGq+tYziLwVQoOXuqYHaBDc3YAAACqm7MDdOiYHYA2KDh4KVfD6potFwUA6E+Z8ebkGL1xFuFFFBx81TqNx7BctDZXFwEA+mXWq+tQziTwRQoOXkJjWtdlWM6n7BAAAFxHmfUuyTF640zCVyk4+KKy0Oc+OUZvPmQHAADg6sx8dd1bNsrXKDj4mu+yA3RmDb/JBADYgzmeZz/qcTbhixQcfNY6jUPYWFzb47CcfdABAHSuzHyP2Tk6cyxnFPgkBQdf8hAR/gGpy1VFAID9MPvVNcTzGQU+ScHBl7gCVtdpWM5P2SEAALiNMvudsnN0xhmFz1Jw8EnrNN5HxF12js54LgwAYH/MgHXdlbMK/IaCg8/xDFNd67Cc5+wQAADcVpkB7WCry1mFT1Jw8BuWi17FnB0A4CtO2QHe4ZQdAOAr5uwAnbFslE9ScPApx+wAHbJgCgBgv8yC9R2zA7A9Cg4+xeKeuh6H5XzJDgHwJcNyPmVneKuWswP7UGZBT8bW5czCbyg4+IV1Gh8i4pCdozMWSwGtaPGlpxYzA/tkJqzrUM4u8A8KDn7Nwp66LsNy1tYDrThlB3iDU3YAgJcoM+ElO0dnnF34BQUH/7BO4yEitKB1aeqBlrT4b1aLmYH98m9WXQ/lDAMRoeDgl47ZATr0U3YAgJcalvNTtPXt4qVkBmiF2bC+Y3YAtkPBwccs6qlrHpazN8+B1vyYHeAVWsoKEGU2nLNzdMYZhn9QcBAREes0HiPCW9J1uYIItOgxIlooZ9fwIgHQJjNiXUM5y4CCg3+woKeuJ88WAi0q3y5+n53jBb53Sw5oUZkR/byuLmcZIkLBQUSs03gXEffZOTrzITsAwFsNy3mObb9OcioZAVplVqzrvpxp2DkFBxF+t1aba9NAD76Pbf5UpZUbJgBf0srPAVviTIOCY+/WaRzC07C1Pbo2DbSuvE6yxSLhey+nAK0rs6IvxOp6KGcbdkzBwUNYLlqbrf5AF8rPQLb0b9qPfpoCdGRL/772wBe3KDiIH7IDdOY0LOdLdgiAWobl/OfYxpOGc8kC0IUyM56SY/TG2WbnFBw7tk7jfUQckmP0xrNfQHeG5fzHyP25yvclA0BvzI51HcoZh51ScOyb55Tqurg6DfRqWM4/RcQf4rZL8daI+EP5vw3QnTI7XpJj9MYZZ8cUHDu1TuMhIo7JMXqjgQe6Niznx4j4XdzmSvUpIn5X/m8C9MwMWdexnHXYIQXHfh2zA3Rozg4AcG3Dcr4My/n3EfH7uM63jpeI+P2wnH9vpxGwE3N2gA4dswOQQ8GxX65u1fVoEAf2ZFjOp2E5fxPPP1upccviMZ5/jvLNsJxPFf73ATShzJBuq9XlrLNT/5QdgNtbp/EhIv49O0dnfm8gB/Zsnca/P8/3rxFxV/7zJU/lP/8RzyXxLXd7AGxKWYz51+wcnfmDnznuj4Jjh9Zp/GtE3Gfn6MilfIsJwEfKb6APv/pvX9x4A/itdRr/Fl44rOlUflLJjig4dqYMm3/LztGZ7234BwDgPdZp/FNE/CU7R2e+Uarvix0c+/NddoAOzdkBAABo3pwdoEPOPjuj4NifY3aAzsx+Nw4AwHuVmXLOztGZY3YAbkvBsSPrNB4jYsjO0ZkP2QEAAOiG2bKuoZyB2AkFx764olXX07Ccn7JDAADQhzJbmi/rcgbaEQXHTqzT+JIn+3gdDTsAALWZMeu6K2chdkDBsR+ay7rWYTnP2SEAAOhLmTHteKvLWWgnFBw7sE7jEBbs1DZnBwAAoFtzdoDOHMuZiM4pOPbhmB2gQ64OAgBwLWbN+o7ZAbg+Bcc+uJJV12lYzpfsEAAA9KnMmqfkGL1xJtoBBUfn1mm8j4hDcozeaNQBALg2M2ddh3I2omMKjv5pKuu6DMv5MTsEAAB9KzPnJTtHZ5yNOqfg6Ng6jYeIeMjO0ZmfswMAALAbZs+6HsoZiU4pOPp2zA7QoZ+yAwAAsBtmz/qO2QG4HgVH31zBqmselrM3yQEAuIkye87ZOTrjjNQxBUen1mk8RoS3nutyRRAAgFszg9Y1lLMSHVJw9Ovb7ACduQzL+ZQdAgCAfSkz6CU5Rm+clTql4OhQWZxznxyjNz9mBwAAYLfMonXdWzbaJwVHn37IDtCZNSI8DQsAQJbHeJ5JqceZqUMKjs6s0ziEp2Fre7RcFACALGUW9YVbXQ/l7ERHFBz9eQjLRWv7kB0AAIDdM5PW5YvhDik4+uPZo7pOw3J+yg4BAMC+lZn0lJ2jM85OnVFwdGSdxvuIuMvO0RnPcgEAsBVm07ruyhmKTig4+uK5o7rWYTnP2SEAACAiosymdsPV5QzVEQVHJ8qCnGN2js74nSMAAFtjRq3raNloPxQc/fhTdoAOzdkBAADgV+bsAB1yluqEgqMfrlbV9Tgs50t2CAAA+FiZUT0ZW5ezVCcUHB1Yp/EhIg7ZOTpjgRMAAFtlVq3rUM5UNE7B0QeNY12XYTlrxQEA2KQyq16yc3TGmaoDCo7GrdN4iAhtY10WNwEAsHVm1roeytmKhik42vdddoAOzdkBAADgK+bsAB1ytmqcgqN9x+wAnZmH5extcQAANq3MrHN2js4cswPwPgqOhq3TeIwIbzbX5aofAACtMLvWNZQzFo1ScLTNFaq6nobl/JQdAgAAXqLMrubXupyxGqbgaNQ6jXcRcZedozMacAAAWmOGreuunLVokIKjXZrFutaI8DQsAACteYznWZZ6nLUapeBo0DqNQ3gatjbLRQEAaI5lo1fxUM5cNEbB0aZjWC5am6t9AAC0yixb1xBeVGmSgqNNrkzVdRqW8yU7BAAAvEWZZU/JMXrjzNUgBUdj1mm8j4hDcoze/JwdAAAA3slMW9ehnL1oiIKjPd9mB+jMZVjOc3YIAAB4jzLTXpJj9MbZqzEKjoas03gIvwWrTdMNAEAvzLZ1HcsZjEYoONpyzA7QoTk7AAAAVDJnB+jQMTsAL6fgaIsrUnXNlosCANCLMtvOyTF64wzWEAVHI9ZpPIblorW5wgcAQG/MuHUdylmMBig42qE5rOsyLOdTdggAAKipzLiX5Bi9cRZrhIKjAWWxzX1yjN58yA4AAABXYtat696y0TYoONrwXXaAzqzht4kAAPRrjueZl3qcyRqg4Ni4dRqHsLm3tsdhOfsHHwCALpVZ9zE7R2eO5WzGhik4tu8hIvwh1eXKHgAAvTPz1jXE89mMDVNwbJ+rUHWdhuX8lB0CAACuqcy8p+wcnXE22zgFx4at03gfEXfZOTrj2SwAAPbC7FvXXTmjsVEKjm3zHFFd67Cc5+wQAABwC2X2tXuuLme0DVNwbJTlolcxZwcAAIAbm7MDdMay0Q1TcGzXMTtAhyxaAgBgb8zA9R2zA/BpCo7tssCmrsdhOV+yQwAAwC2VGdiTsXU5q22UgmOD1ml8iIhDdo7OWLAEAMBemYXrOpQzGxuj4Ngmi2vqugzLWWsNAMAulVn4kp2jM85sG6Tg2Jh1Gg8RoQ2sS2MNAMDemYnreihnNzZEwbE9x+wAHfopOwAAACQzE9d3zA7ALyk4tsfCmrrmYTl7+xsAgF0rM/GcnaMzzm4bo+DYkHUajxHhTeW6XMUDAIBnZuO6hnKGYyMUHNtiUU1dT8NyPmWHAACALSiz8VN2js44w22IgmMj1mm8i4j77Byd+ZAdAAAANsaMXNd9OcuxAQqO7fD7rbrWiPA0LAAA/NJjPM/K1OMstxEKjg1Yp3EIT8PW9mi5KAAA/FKZkX0RWNdDOdORTMGxDQ9huWhtP2YHAACAjTIr1+UL641QcGzDD9kBOnMalvMlOwQAAGxRmZVPyTF640y3AQqOZOs03kfEITlGbzx/BQAAX2ZmrutQznYkUnDk86xQXZdhOc/ZIQAAYMvKzHxJjtEbZ7tkCo5E6zQeIuKYHKM3mmgAAHgZs3Ndx3LGI4mCI9cxO0CH5uwAAADQiDk7QIeO2QH2TMGRyxWmuh4tFwUAgJcps7MnY+tyxkuk4EiyTuNDWC5a24fsAAAA0BgzdF2HctYjgYIjz3fZATpzGZbzKTsEAAC0pMzQl+QYvXHWS6LgSFAWz9wnx+iN5hkAAN7GLF3XvWWjORQcOTR69c3ZAQAAoFFzdoAOOfMlUHDkOGYH6Mw8LOc1OwQAALSozNJzdo7OHLMD7JGC48bWaTxGxJCdozOu1AEAwPuYqesaytmPG1Jw3J6rSnU9Dcv5KTsEAAC0rMzU5uq6nP1uTMFxQ+s03kXEXXaOzmiaAQCgDrN1XXflDMiNKDhuS4NX1zos5zk7BAAA9KDM1nbb1eUMeEMKjhtZp3EIi2Zqm7MDAABAZ+bsAJ05lrMgN6DguJ1jdoAOuUIHAAB1mbHrO2YH2AsFx+24mlTXaVjOl+wQAADQkzJjn5Jj9MZZ8EYUHDewTuN9RBySY/RGswwAANdh1q7rUM6EXJmC4zY0dnVdhuX8mB0CAAB6VGbtS3aOzjgT3oCC48rWaTxExEN2js78nB0AAAA6Z+au66GcDbkiBcf1HbMDdOin7AAAANA5M3d9x+wAvVNwXJ+rSHXNw3L2NjcAAFxRmbnn7BydcTa8MgXHFa3TeIwIbx7X5aocAADchtm7rqGcEbkSBcd1fZsdoDOXYTmfskMAAMAelNn7khyjN86IV6TguJKyQOY+OUZvfswOAAAAO2MGr+vestHrUXBczw/ZATqzRoSnYQEA4LYe43kWpx5nxStRcFzBOo1DeBq2tkfLRQEA4LbKDO6LxroeypmRyhQc1/EQlovW9iE7AAAA7JRZvC5fiF+JguM6XDmq6zQs56fsEAAAsEdlFj9l5+iMJ2OvQMFR2TqN9xFxSI7RG89TAQBALjN5XXfl7EhFCo76PPtT1zos5zk7BAAA7FmZye3Eq8vZsTIFR0VlUcwxO0dn/N4PAAC2wWxe19Gy0boUHHX9KTtAh+bsAAAAQESYza/BGbIiBUddrhjV9Tgs50t2CAAAIKLM5p6MrcsZsiIFRyXrND6E5aK1WWQEAADbYkav61DOklSg4KhH81bXZVjO2mEAANiQMqNfsnN0xlmyEgVHBes0HiJC61aXBUYAALBNZvW6HsqZkndScNTxXXaADs3ZAQAAgE+aswN0yJmyAgVHHcfsAJ2Zh+XsjW0AANigMqvP2Tk6c8wO0AMFxzut03iMCG8X1+XKGwAAbJuZva6hnC15BwXH+7lKVNfTsJyfskMAAACfV2Z2c3tdzpbvpOB4h3Ua7yLiLjtHZzTBAADQBrN7XXfljMkbKTjeR8NW1xoRnoYFAIA2PMbzDE89zpjvoOB4o3Uah/A0bG2WiwIAQCMsG72Kh3LW5A0UHG93DMtFa3PFDQAA2mKGr2sIL6q8mYLj7Vwdqus0LOdLdggAAODlygx/So7RG2fNN1JwvME6jfcRcUiO0ZufswMAAABvYpav61DOnLySguNtNGp1XYblPGeHAAAAXq/M8pfkGL35NjtAixQcr7RO4yEsF61N4wsAAG0z09d1LGdPXkHB8XrH7AAdmrMDAAAA7zJnB+jQMTtAaxQcr+eqUF2z5aIAANC2MtPPyTF64+z5SgqOV1in8RiWi9bmKhsAAPTBbF/XoZxBeSEFx+to0Oq6DMv5lB0CAAB4vzLbX5Jj9MYZ9BUUHC9UFrzcJ8fozYfsAAAAQFVm/LruLRt9OQXHy3katq41/EYPAAB6M8fzrE89zqIvpOB4gXUah7DBtrbHYTn7hw8AADpSZvzH7BydOZYzKV+h4HiZh4jw/1B1uboGAAB9MuvXNcTzmZSvUHC8jCtBdZ2G5fyUHQIAAKivzPqn7BydcSZ9AQXHV6zTeB8Rd9k5OuP5KAAA6JuZv667cjblCxQcX+dZnrrWYTnP2SEAAIDrKTO/nXt1OZt+hYLjCywXvYo5OwAAAHATc3aAzlg2+hUKji87ZgfokIVDAACwD2b/+o7ZAbZMwfFlFrnU9Tgs50t2CAAA4PrK7O/J2LqcUb9AwfEZ6zQ+RMQhO0dnLBoCAIB9cQao61DOqnyCguPzLHCp6zIsZ+0tAADsSDkDXLJzdMZZ9TMUHJ+wTuMhIrRidWluAQBgn5wF6nooZ1Z+RcHxaX7XVN9P2QEAAIAUzgL1HbMDbJGC49OO2QE6Mw/L2RvYAACwQ+UsMGfn6Iwv5T9BwfEr6zQeI8LbwnW5kgYAAPvmTFDXUM6ufETB8VsWttT1NCznU3YIAAAgTzkTPGXn6Iyz668oOD6yTuNdRNxn5+jMh+wAAADAJjgb1HVfzrAUCo5f8jumutaI8DQsAAAQ8Xw2sJuvLmfYjyg4inUah/A0bG2PlosCAAAR/1g26gvQuh7KWZZQcHzsISwXre3H7AAAAMCmOCPU5Yv6jyg4/ssP2QE6cxqW8yU7BAAAsB3ljHBKjtEbZ9lCwRER6zTeR8QhOUZvPAMFAAB8irNCXYdypt09Bcczz+vUdRmW85wdAgAA2J5yVrgkx+iNM20oOGKdxkNEHJNj9EYjCwAAfIkzQ13Hcrbdtd0XHKHcuIY5OwAAALBpc3aADh2zA2RTcLjKU9uj5aIAAMCXlDODJ2Pr2v3ZdtcFxzqND2G5aG0fsgMAAABNcHao61DOuLu164IjIr7LDtCZy7CcT9khAACA7Stnh0tyjN7s+oy724KjLGC5T47RGw0sAADwGs4Qdd3vednobguO2HmzdSVzdgAA+P/t3U1yY1W2NuAX4vav7ggQEeqXawSIEeDqqpNiBEmNIGEEwAgQHXVxjQAxAkxfEahGUKdG8H2Ns4WVTmemf460z8/zRDhsnP5ZVZCy16u11wZgUDa1Cxihyfa6kww4mtViFhtmu7aZbfdN7SIAAIDhKD3EpnYdI7OuXUAtkww4klwnmdUuYmSMlgEAAM+hl+jWrFkt1rWLqGGqAcdkR3bO5Ha23d/WLgIAABie0kvoJ7o1yZ53cgFHs1pcJbmqXcfISFwBAICX0FN066r0vpMyuYAjE02yzqiZbfeb2kUAAADDVXoKO/26Nbned1IBh+WiZ7GpXQAAADAKm9oFjMy69MCTMamAI8KNczBKBgAAdEFv0b117QIuaWoBx+RGdM5sN9vuD7WLAAAAhq/0FrvKZYzNpHrgyQQczWqxTDKvXMbYSFgBAIAu6TG6NS+98CRMJuDIxJKrCzjMtvub2kUAAADjUXqMQ+06RmYyvfAkAo5mtZgnua5dx8j8XLsAAABglPQa3bouPfHoTSLgyMQWq1zID7ULAAAARkmv0b117QIuYSoBx2RGci5kM9vu3VENAAB0rvQam9p1jMwkeuLRBxzNarFOMqm7fy/AyBgAAHBOeo5uzUpvPGqjDziSvKpdwMgcZtv9rnYRAADAeJWe41C5jLEZfW886oCjLFJZVi5jbL6rXQAAADAJeo9uLce+bHTUAUeSN7ULGJkmiathAQCAS7hJ24PQnVH3yKMNOJrVYhZXw3btxnJRAADgEkrv4QnWbl2XXnmURhtwpA03RvsvrhIjYgAAwCX9WLuAkRn1IMCYA45Rj95UsJtt94faRQAAANMx2+5vk+xq1zEyo70ydpQBR7NaLJPMK5cxNq5pAgAAatCLdOuq9MyjM8qAIxO4/ubCmtl2v6ldBAAAMD2lF7ELsFuj7JlHF3CUhSnr2nWMjHNvAABATXqSbq3HuGx0dAFHkm9qFzBCm9oFAAAAk7apXcAIja53HmPAMcpRm4puLBcFAABqKj2JK2O7NbreeVQBR7NaXMdy0a5Z6AMAAPSB3qRb89JDj8aoAo6MMIGq7DDb7qWkAABAdaU3OdSuY2RG1UOPJuBoVot5klGlTz1gkQ8AANAnepRuXZdeehRGE3AkeV27gBHa1C4AAADgxKZ2ASM0ml56TAHHunYBI7OZbffumgYAAHqj9Cib2nWMzLp2AV0ZRcDRrBbrJKO7w7cyo18AAEAf6VW6NSs99eCNIuDIiEZqeuJ2tt3f1i4CAADgvtKr6Fe6NYqeevABR7NaXCW5ql3HyEhEAQCAPtOzdOuq9NaDNviAIyNJmnqkSeJqWAAAoM9u0vYudGfwvfWgA45mtZjF1bBds1wUAADoNctGz+K69NiDNeiAI+2210H/C+gho14AAMAQ6F26NcvAb1QZesAx+BGantnNtvtD7SIAAAA+pvQuu8pljM2ge+zBBhzNarFMMq9cxthIQAEAgCH5uXYBIzMvvfYgDTbgyMCTpR46zLZ7y0UBAIDBmG33mySHymWMzavaBTzXIAOOZrWYx3LRrkk+AQCAIdLLdGtdeu7BGWTAkYEvPumpTe0CAAAAnmFTu4ARWtcu4DmGGnAMdmSmpzaWiwIAAENUeplN5TLGZpA99+ACjma1WMdy0a4Z6QIAAIZMT9Oteem9B2VwAUcGmiT12GG23e9qFwEAAPBcpac5VC5jbAbXew8q4CiLTpaVyxgbV8MCAABjoLfp1nJoy0YHFXDE1bBda+KsGgAAMA6btD0O3RlUDz6YgKNZLWYZ6CbXHruZbfceAAAAgMErvc1N7TpGZl168UEYTMCR5DrJYP6PHQgjXAAAwJjocbo1S9uLD8KQAo5BjcYMwG623d/WLgIAAKArpcfZ1a5jZAbTiw8i4GhWi2WSq9p1jIxrlAAAgDHS63TrqvTkvTeIgCMDvJ6m55rZdr+pXQQAAEDXSq9j12C3BtGT9z7gsFz0LDa1CwAAADijTe0CRmYQy0Z7H3BEuHEOFu8AAABjpufp3rp2AR8zhIBjMAtNBuJmtt0fahcBAABwLqXncWVst3rfm/c64GhWi+sk89p1jIyFOwAAwBTofbo1Lz16b/U64MhAFpkMyGG23UsxAQCA0Su9z6F2HSPT6x69twFHs1rMk/Q6HRog59AAAIApMcXRrevSq/dSbwOODOB8zwBtahcAAABwQT/ULmCE1rULeJ8+Bxzr2gWMzGa23bsLGgAAmIzSA21q1zEyvR1G6GXA0awW6yS9v2N3YIxmAQAAU6QX6tas9Oy908uAIz1fXDJAt7Ptfle7CAAAgEsrvdBt7TpGppc9e+8Cjma1uEqyrF3HyFguCgAATJmeqFvL0rv3Su8CjvT4PM9ANUlcDQsAAEzZTdreiO70rnfvVcDRrBazuBq2azeWiwIAAFNWeiJP/HbruvTwvdGrgCNtuNGr/4NG4LvaBQAAAPSA3qhbvRtQ6FvA8aZ2ASOzm233h9pFAAAA1FZ6o13lMsamVz18bwKOZrVYJplXLmNsXIcEAABwR4/UrXnp5XuhNwFHenrNzIAdZtv9pnYRAAAAfVF6pEPlMsamN718LwKOZrWYJ1lXLmNsJJMAAADv0it1a116+up6EXBEuHEOm9oFAAAA9NCmdgEjtK5dQNKfgKM3Iy0jcWO5KAAAwLtKr+TK2G71oqevHnA0q8V1LBft2o+1CwAAAOgxPVO35qW3r6p6wJHkde0CRuYw2+53tYsAAADoq9IzHSqXMTbVe/uqAUdZRLKsWcMISSIBAAA+Tu/UrWXtZaO1JziqJzwj08TCHAAAgMfY1C5ghKr2+NUCjma1mKUnm1ZH5Ga23Te1iwAAAOi70jttatcxMuua37zmBMd1klnF7z9GRqwAAAAeTw/VrVmzWqxrffOaAYfjKd26nW33t7WLAAAAGIrSQ+mjulWt168ScDSrxVWSqxrfe8QkjwAAAE+nl+rWVen5L67WBIfpjW41s+1+U7sIAACAoSm9lF2G3arS81884LBc9Cw2tQsAAAAYsE3tAkZmXXr/i6oxwbGu8D3HzkgVAADA8+mpure+9DesEXA4ntKt3Wy7P9QuAgAAYKhKT7WrXMbYXLz3v2jA0awWyyTzS37PCZA0AgAAvJzeqlvzkgFczKUnOExvdOsw2+5vahcBAAAwdKW3OtSuY2QumgFcLOBoVot5kutLfb+J+Ll2AQAAACOix+rWdckCLuKSExzrC36vqfihdgEAAAAjosfq3vpS3+iSAYfjKd3azLZ7dzUDAAB0pPRYm9p1jMzFsoCLBBzNarFOcvE7cEfO6BQAAED39FrdmpVM4OwuNcHx6kLfZyoOs+1+V7sIAACAsSm91qFyGWNzkUzg7AFHWSiyPPf3mZjvahcAAAAwYnqubi0vsWz0EhMcby7wPaakSeJqWAAAgPO5Sdt70Z2zZwNnDTia1WIWV8N27cZyUQAAgPMpPZcnlrt1XTKCszn3BMd1LBftmlEpAACA8/uxdgEjc/YBiHMHHI6ndGs32+4PtYsAAAAYu9l2f5tkV7uOkTnrlbFnCzia1WKZZH6urz9RrisCAAC4HD1Yt65KVnAW55zgcDVst5rZdr+pXQQAAMBUlB7MDsRunS0rOEvAURaHrM/xtSfM+S8AAIDL04t1a32uZaPnmuD45kxfd8o2tQsAAACYoE3tAkboLJnBuQIOx1O6dWO5KAAAwOWVXsyVsd06S2bQecDRrBbXsVy0axbbAAAA1KMn69a8ZAedOscEh+mNbh1m2720EAAAoJLSkx1q1zEynWcHnQYczWoxT9J5CjNxFtoAAADUpzfr1nXJEDrT9QTH646/HhbaAAAA9MGmdgEj1GmG0HXAse74603dZrbdu3MZAACgstKbbWrXMTLrLr9YZwFHs1qsk5zlLtsJMwIFAADQH3q0bs1KltCJLic4HE/p1u1su7+tXQQAAACt0qPp07rVWZbQScDRrBZXSa66+Fr8RTIIAADQP3q1bl2VTOHFuprgML3RrSaJq2EBAAD65yZtz0Z3OskUXhxwNKvFLK6G7ZrlogAAAD1k2ehZQq1o2wAAGvRJREFUXJds4UW6mOBYx3LRrhl5AgAA6C89W7dm6eBGlf95eR2Op3RsN9vuD7WLAAAAGIsyHfDQnof3vf9RXzae7O/S6yQ/vOQLvCjgaFaLZZL5S74G75AEAgAAvEezWsxz14de5S5k+OLkw04/hmGYN6vFcrbd7577BV46wWF6o1uH2XZvuSgAADBZ5Yn0JDm+PgYXp2EG4/Qqye65n/zJcz+xpGZ/PvfzedB3s+3+29pFAAAAnFO5FnSeNrT47ORtAQafP3dtw0smONYv+FwetqldAAAAQFfuBRl/O3kb3med5NvnfOJLAo5XL/hc3rWxXBQAABiqEmYcg4yr3B0xgad4lUsGHM1qsY6FLV37uXYBAAAAj1FWFlyl3Y8hzKBL82a1WM+2+81TP/FZOzia1eLX+A+4a7vy+pDk3yfvv017/VCSNLPt/vaCNQEAABynM5a5CzTmNeth9Haz7f7Lp37SkwMOy0V741BekjYE+W95e3d832y7bwIAAPBE9wKNZSz/5PKevGz0OUdUXA3bD/PcpabLk/e/Ob7RrBbJXRDSJPmjvL5NeyXt4cw1AgAAA1CeyF6mDTSuI9CgvtdJ/vmUT3jSBEezWszSTm/4j308DuXlOAWyi6MwAAAwes1qsUzyVdpgw80m9E2Tdorj0ScTnhpwrJP89MSiGK7btOHHH7mb+hB8AADAAJUnrK9jSoPh+Popy0afGnD8Hske7wYft467AABA/5SjJ6ehBgzJ7Wy7//tjP/jRAUcZX/r1ORUxCcfdHr+lHHkx7QEAAJd3Emq8iieoGb4vZ9v97jEf+JQlo6+eVwsTMUt7dm95fEezWpyGHiY9AADgTE6On7yOUINxeZW720I/6FETHOUvy39eUBAcHXISejw2iQMAAN5V9iR+FcdPGLf/e8yy0cdOcKxfVgv8ZV5erpO/rrLdpQ08dmlDj0dvyQUAgKkp6wNexaJQpmOd5IePfdBjJzj+TNuUwiXc5iT0EHgAADB1J3s1XkdvxvQcZtv95x/7oI8GHM1qcZ3kl05KgufZ5S7s2NUtBQAALqf0Y8dpDZiyf8y2+5sPfcBjAo5f4i8T/XKTu8DDTS0AAIxK2YH4TdpgY163GuiNm9l2/48PfcAHA44yBvVnlxVBxw5pJzz+FcdZAAAYsGa1uEp7BGVduRToq88/dDPnx5aMvu62FujcPO0PgHWSNKvFcbrjxpW0AAAMQbkJ5VWSZd1KoPfWSb593x9+bILjP7GVl+E6Liv92VEWAAD6pBxDWcfSUHiKZrbd/9/7/vC9AUdJEX86R0VQwSFld8fHFtMAAMC5nOzXeB1PJsNzfD3b7jcP/cGHAo5fY0SKcWrShh3/EnYAAHAJZb/hm7QXOAg24Pl2s+3+y4f+4MGAoyy3+f2sJUE/CDsAADibk2BjXbcSGJW/P7SG4H1LRi0XZSqOZx/XzWrRJNnEzg4AAF5IsAFn9TrJ1/ff+c4ERzkT9meMTTFth7STHT+6jQUAgMcSbMBFNGmvjG1O3/lQwLGO5aJw6jbJj2mvnm0+9sEAAEyP5aFwce8sG30o4PgzrimC99nEvg4AAE40q8W3EWzApR1m2/3np+94K+BoVotlkl8vWREM1CHJz0k2jrAAAExTs1pcJ/k+niCGWr6cbfe74z98eu8PX122FhisedqzlX82q8Uv5YcbAAAT0KwWV81q8WuSXyLcgJreyjD+muAoy3D+vHQ1MCKHmOoAABitsmfj+1ggCn3y+bH/Op3g8Aw0vMw8d1MdP5UjXwAAjECzWnyT9gnhdeVSgLetj2+cTnBYLgrdu0171eymdiEAADxds1pcpb1l8qp2LcCD/lo2+kny11/a36uWBOPWpL1q9gdXzQIA9F85jvIm7dWvQL/9fbbd3x6PqDieAud1/AH5n3J8ZV65HgAA3qMcNf49wg0YiuvkbgfHVxULgalZ5+72lWXlWgAAKJrVYtasFr8k+TWO78OQfJUkn5TRq/9ULgambJfku9P7mwEAuKxmtbhOu2tjVrsW4Fn+75PyDPKvtSsBBB0AAJfm6lcYjS8/TbKsXQWQpP27+GuzWvzq6AoAwPmd7NpY160E6MDy0ySf1a4CeMsygg4AgLNqVotvY9cGjMln/xN/oaGvlkmWzWqxi6MrAACdKLfZ/ZLkqnIpQLfmnzSrxX9ikQ4MwS7J17Pt/lC5DgCAQWpWi3XafRv6Hxif5pNmtfh/tasAnmSTdqLjULkOAIBBsEgUpkHAAcPUJPkxyQ+z7b6pXQwAQF85kgLT8WntAoBnmSV5k+TPMmoJAMA9zWpxnfaWFOEGTICAA4ZtluSnZrX43Y0rAAB3yi0pv8S+DZgMR1RgXG6S/NN+DgBgqsq+jZ+SXNeuBbgsExwwLtdJfi/PWAAATEqzWlwl+TXCDZgkExwwXoe018ruKtcBAHB25biuIykwYSY4YLzmSX5tVotfyqgmAMAolaXrv0a4AZP2aZLb2kUAZ3Wd9raVb2oXAgDQtWa1+Cntzg1g2m7/J0lTuwrg7GZJvm9Wi6/SLiEVbAIAg2aZKHBP82mS32pXAVzMMpaQAgADV8INy0SBU799mnYRITAtb5rV4veyaRwAYDDK7y+/J/F7DHDq8EmzWsyT/Fm7EqCa75L8MNvuHVcDAHrt5BpYy0SB+z7/JEma1eI/8SABU3aIK2UBgB4rN6V8H30L8K5mtt3/3/Ga2JuqpQC1zdNeKftt3TIAAN5Vwo2fItwAHnaTtNfEJhaNAi27OQCAXilX3bsGFviQ35K7gMMEB3B0FTetAAA90KwWP6U9lgLwITdJ8snxn8qDx7pWNUAv3abdzXFbuxAAYFr0J8AjbWbb/dfJ3QRHkvxcqRigv47THN/ULgQAmIZmtZgJN4An+CvL+OT0vc1q8WfaZYMA9+3STnMcKtcBAIxUs1rM0l4Dax8Y8BiH2Xb/+fEfPr33h99duBhgOJZppznWlesAAEZIuAE8w1sZxif3/9QUB/AIN2mnOZrahQAAwyfcAJ7hremN5N0JjsQUB/Bx10n+bFaL69qFAADDVq6n/z3CDeBp3sku3pngSJJmtfg17Tg6wMf8kOQ70xwAwFOVcOPXJLPatQCDsptt91/ef+dDExxJ8s8zFwOMxzdpd3MsaxcC1FeaFYCPEm4AL/BgZvHgBEeSNKvF92kbF4DHMs0BlT0QNl7l3ebhs7x/39b9z6/hNsn7Hkd+e8THN7Pt/rbzqoDOlKXl30e4ATzdD7Pt/mkBR5I0q4WzcMBTHdIuIN1VrgMG715Ycfr2FydvPxRg8LYmbQhydBqSnIYjtwJaOL8SbvxUuw5gkG5n2/3f3/eHHws4jI0Bz2WaA96j/HydpZ2imCf539w9oSCw6Idded0k+aO8fQxDDrPt/lChJhi8ZrX4Ju3kBsBTNUm+/NCU5gcDjkTCCrzIIaY5mKAyeTFLG1achhfLSiVxHqeTIcepkF2SeNyDdzWrxU9J1rXrAAbr69l2v/nQB3w04EgkrcCLmeZgVJrV4hheHKct/lZeLyuWRT/tyuvfcheImABhUspj5vcRbgDP98/Zdv/Dxz7oUQFHInEFXqxJm7re1C4EHuveJIYQg64dj7ychh/2gDAqJdz4Nfb6Ac+3mW33Xz/mAx8dcCRCDqATu7RBx6FyHfCXEmTMy8sXJ29DLbu0x/z+fXzb4yZDU/YN/RThBvB8jw43kicGHImQA+jMd2mvePJMJRfTrBbz3B0t+VvaEMMv3gzJLm8HHyY+6KUSHP8SS5OB53tSuJE8I+BILB4FOnNIe57OsRU6V545PAYZp/syYGyOx1t+S/u4evuhDfNwbvb3AR346ELRhzwr4EikskCndmmDDr+Q8ywPhBnLqgVBP+zSBh9/ROjBBVgmygAdysuH/Hby9mf5+BFWT6i8TJPkH8+9jezZAUfy14PYL/GLJNCNTdrbVg6V66DHyjGTZYQZ8By7CD04g/LY/Esc+6OuXXl9SHuU7/R9SYVjfSc3rx0tT97+oryex+6vpP139Y+X/Dt6UcBxVMbQ3kRSBXTDfg6SvPVLwTLtLwGeFYHu7dI+Q3m8xeVQtRoGp1ktrtMeX/f4zLn9deNUkv+mhBfPfba/b+6FIcvy+ovc3eg2Vk3aJzk/eg3sx3QScCR/pbZvYiQN6EaT5McIOibl5KjJMcwY8w9z6KtD7nZ63I6lcaB7pRl7k+Sb2rUwOqdBxh9pb5LaVa2oB0rPPc/dEz5juPltkw4nuDsLOI7Kbo43MTIMdEPQMWLlZ8Yy7XGTZTz7B321y92Ux87jMa6ApUOni5JNkj3TA1fe933qdZc22Nh1+UU7DziOyoPe65joALoh6Bi48kzfMnc/dJc16wFe5DbtL6d/pA08DlWr4aKa1eLbtE9ownMc8vbjh11AZ3LvqO9xd9m8YklJO7Hx47n+vZ8t4Dgq/6euk7zKcBLeQz6+Tfd95qn/Hw2M3SaWkfbevWWgywznZwDwdIfcTXkIPEbK1AbP1CS5iceHXqi03+w2yc9JNud+ovLsAcepk192vyiv5x1++UPuQokmbSKYk3++nxDV2KA7z7v/m5cnb/9v7n5gjH2RDHThJm0CvKtdCGd/jAeG5ZC7kXPP0A6cXRs8wy7Jv+Lv/yCcYQfaIZVC74sGHPedpEfH1OhveX96dMi7V/00U/gLc2+b7vH/q+MdzIIQaB8fvkty4/jK5ZQfhsvcBRp9PucJ1NXk7V92R//721iUG1K+j9CaDzud0vD72Ajc2+lx7D0fckjbp/+1GLbmv/+qAQfdOQlB5nn7P8K+L5eBLh1/uJ7tXN+UlR90x3R/GY8twPMd4khLr5UQ+/vYl8T7HX/v+tdsu7+pXQwkAo5JeCD8GMN1QvAxFzvrN1YnN5wcAw2AczlE4NEL5bjhm7gogIcJNeg1AcfEnYweHbfqLiuWA+dy/EG8qV1IX7nhBOiZQ94+wy+oPrPyc+CbuB2Fh+3SPnHk+Am9JuDgHSW5P100s6xZD3TIsw7FvWVSy5joAvrteC3tccJDg9WRk2DjdRw95G2H3E3DHuqWAo8j4OBRzrBZF/rgJnfPDh4q13I2la4DAzinvwKPqQfWz1We0FpHsMG7bpL87O8WQyTg4FlOxtlNeTAWo/hl+V6YcTx6Nq9YEsAl7HI33bGrW0q/2bHBezRJNmkXtR/qlgLPJ+CgMycLCb+KCQ+G73Qc+raPP+zLZNU8d0HjPMIMgETg8Y5y3evreFKKtx2S/BhL2RkJAQdncW9h4XU0XQzfX3d7p73r+zbJ4dzBRwkxjlMZswgyAJ5jlxJYZ0I7PE6OobyKnxu87ZDkOwvYGRsBBxdRfsAu0053LOOsJ+NyKC9J+8vzf9/zZ6ce2oPxRXl9DDQAOI/jlN4fGdkepvIk03XaUGNZtxp6aJc22NhVrgPOQsBBFWVM0nQHANAHh7ShxzHw2FWt5onuPZF0XbUY+moXwQYTIOCgujKCfx27OwCA/jgeS/zj+HZfjracLJQ+Tsb6/Yn3OcRRFCZEwEGvlGcgjmOVflgDAH1Sax/TMu3vRZ9FoMHjNGmDjR9qFwKXJOCgt4QdAMCAHMpLk3bqI7kLRB7jdP/SZ7m7JcveMp7qh7ThRi8mjuCSBBwMwknY8Tp2dgAAwH27JF+PaWkuPJWAg8EpOztepb32zLMaAABMWZPkn/ZsgICDgSu3sbyKjeEAAEzPTdqpDcdRIAIORsIRFgAAJqRJG2zc1C4E+kTAweiUTeOvY6oDAIDxMbUB7yHgYLTKVMc6bdhhVwcAAEPm6lf4CAEHk9CsFuskb+L4CgAAw3ObdmrjsdcOwyQJOJiUcnzlTZJl3UoAAOBRNmlvSXEkBT5CwMEklatmX6c9wgIAAH30T0dS4PEEHExa2dPxJoIOAAD6o0nypSMp8DQCDoigAwCA3rhN8o/Zdn+oXQgMjYADTgg6AACoyBWw8AICDnhA2dHxfSwjBQDgMjaz7f7r2kXAkAk44APKrSvfJ7mqXAoAAOP19Wy739QuAoZOwAGP0KwW67RBx6xyKQAAjEeT9qaUTe1CYAwEHPBIzWoxS/JN2h0dAADwEm5KgY4JOOCJyiLSn2I/BwAAzyPcgDMQcMAzNavFddpjK/PKpQAAMByugYUzEXDAC5RjK2/SHl0BAIAPuU07ueEaWDgDAQd0oFwr+1PctgIAwMOEG3BmAg7oULNafJvkddy2AgDAnU3a21KEG3BGAg7omCWkAACc2My2+69rFwFTIOCAM2lWi+OVsqY5AACmSbgBFyTggDMyzQEAMFnfzbb7b2sXAVMi4IALKNMc39euAwCAi/h6tt1vahcBUyPggAtx0woAwCQIN6ASAQdcWLlp5U3lMgAA6FaT9qaUTe1CYKoEHFBBmeb4Jcm8cikAALxck+TL2XZ/W7sQmLJPaxcAU1R++P09yQ+1awEA4EWEG9ATJjigsma1WKbdzTGvWwkAAE90m3bnhnADekDAAT3QrBaztCHHde1aAAB4lNu0kxtN7UKAloADeqRZLa7TBh2z2rUAAPBeN2knN4Qb0CMCDugZ0xwAAL22mW33X9cuAniXgAN6qlktvkl7naxpDgCAfvhutt1/W7sI4GECDuixZrWYp53mWNatBABg8r6ebfeb2kUA7yfggAEwzQEAUI1rYGEgBBwwEKY5AAAu7jbJP2bb/aF2IcDHCThgYExzAABchJtSYGAEHDBAZZrj+7hpBQDgHCwThQEScMCANavFddpjK6Y5AABerkk7tXFTuxDg6T6tXQDwfOWH7+dJNpVLAQAYutu0y0SFGzBQJjhgJJrVYpl2mmNetxIAgMGxbwNGQMABI9OsFt8meR3HVgAAHuOfs+3+h9pFAC8n4IARsoQUAOCjDmmvgL2tXQjQDQEHjJhjKwAAD3IkBUZIwAET0KwW3yR5E8dWAIBpa9IeSdnULgTonoADJqJZLWZpQ45vatcCAFDBbdojKYfahQDnIeCAiSn7OX5KsqxbCXAhh/Jy+s///sjHnGrOeT69WS2u8v7pslmSqwfe/7d7nzOPo3jAh3032+6/rV0EcF4CDpiosp/jTQQdMDS78rpJ8sfJ28cQ4qyBxBDcC03muQs/Pjt5+/T9wHjdpt21MenHRZgKAQdMXLNarNMGHfO6lcDknYYUv5XXt+X9ByPV51MC3+Qu9Pjf3E2OLN/5BGAoTG3AxAg4gCSCDriQY2BxDDB2MXExCCchyHE65Ivyz8uHPh6oapd2kajHVpgYAQfwFkEHvNihvPyWu6kMExgjVpY4X+VuAuS4I2RZrSiYpibt1MYPtQsB6hBwAA8SdMBHHXIXZBzShhi7euXQR2Wx8zxtAPJZef2hxarA89yk3bXR1C4EqEfAAXyQoAOStOPOt2lvH7kVZPBSJ1MfV2knPuYx8QHPcUgbbOwq1wH0gIADeJQSdLzOw1c2wlgcj5T8Vl7fOlrCJZUbYOZpH2u/iGkPeB/HUYB3CDiAJ3G9LCMizGAQyjGX47SH0AOSTdoloo6jAG8RcADPUn7hfpNkXbcSeLRd2iDjjyQ7YQZD9kDosaxZD1zILu1xlEPlOoCeEnAAL1LOkX+T5FXs6aA/DjmZznA2mykox1uWaXd6LOMxmfHYpT2OsqtcB9BzAg6gM2VPx6t4JpHLu037C7DpDChKAL2MKQ+G65A22NhUrgMYCAEH0LnyLOLrJNdxTpzz2KWdztilndBwDhseoexROg08PEbTR4cINoBnEHAAZ1OePbyO21d4mSZ3gYbjJtChk2Mtx+Wl85r1MHmHCDaAFxBwABdRFuIdpzrmVYuh7w55+7jJbdVqYELKY/UydxMe83rVMCGHCDaADgg4gItrVovrJF/FDSy0Djk5cmJ/BvSHwIMz2yX5WbABdEXAAVRzcoTlq/KaabAQFAbq3pGWZezw4Hl2cSsKcAYCDqAXhB2jtsvdla07C0FhPAQePEGT5CZtsHGoXAswUgIOoHfuhR3L+IV5SJq8HWbs6pYDXNJJ4HF8/IZDkh+TbATcwLkJOIDeKzs7vogFpX10iIWgwHuUa2mXuZvwYDpu0u7XuKldCDAdAg5gUMrCu2PgsYzpjkvbpVzXGsdNgCc6CTy+iuvDx+iQdlrjxjEUoAYBBzBonh08q13aIOOPJLemM4AuleOIy9w9fgs8hum4W+NHPyeA2gQcwKjcW3h3FUdaHuO4N0OYAVRzL/A4PpbTT8dQ41+OoAB9IuAARu3kF+arJH+L0GOXdoT438e3jREDfWVKr1eEGkDvCTiAySmhx1V5+Sx3oce8XlWdOpSX2yT/jSADGAlTehd3SBtq/CbUAIZAwAFwovzyfJz6SNqpj1n6E4Acj5MkdwHG4fgixACmpCyevopjLV26yd1V344rAoMi4AB4opMQJOX1Q4vxvnjsl0u79+K+2/JnSdL4JRPgccpj9OmxxGXVgvpvl7tAY1e3FICXEXAAADBqJfSY527aY55+TOVd2iFtgP5b2oXSu6rVAHRMwAEAwCSVJabz8vJF3j+VN0S7tIHGHyk3Zc22++ZDnwAwdAIOAAA4UXZ7nL58Vl73LQDZlde/5W5Hk31MwGQJOAAA4Inu7WM6fTu5W1D9Er+dfrvcLZgWYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAY/x8UMsYnnxnA1AAAAABJRU5ErkJggg==" - }, - "asset-58ae3445-4001-45e7-9603-19ec8d41e64e": { - "id": "asset-58ae3445-4001-45e7-9603-19ec8d41e64e", - "@created": "2018-09-06T20:18:30.635Z", - "type": "dataurl", - "value": "data:image/png;base64,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" - } - } - } - } -] diff --git a/x-pack/plugins/canvas/server/sample_data/flights_saved_objects.json b/x-pack/plugins/canvas/server/sample_data/flights_saved_objects.json deleted file mode 100644 index a93d34bce5098..0000000000000 --- a/x-pack/plugins/canvas/server/sample_data/flights_saved_objects.json +++ /dev/null @@ -1,512 +0,0 @@ -[ - { - "id": "workpad-a474e74b-aedc-47c3-894a-db77e62c41e0", - "type": "canvas-workpad", - "updated_at": "2018-10-22T14:17:04.040Z", - "version": 1, - "migrationVersion": { - "canvas-workpad": "7.0.0" - }, - "attributes": { - "name": "[Flights] Overview", - "width": 1280, - "height": 720, - "page": 0, - "pages": [ - { - "id": "page-261eb6da-4ab2-400d-b1be-b72cbbcf58ff", - "style": { "background": "#f4f4f4" }, - "transition": { "name": "" }, - "elements": [ - { - "id": "element-5929e53d-4dad-49a5-a432-8de3b1d05b82", - "position": { - "left": 855.5, - "top": 185.312409153891, - "width": 407, - "height": 140.250363384436, - "angle": 0 - }, - "expression": "\nshape \"square\" fill=\"#FFFFFF\" border=\"rgba(255,255,255,0)\" borderWidth=0 maintainAspect=false\n| render\n" - }, - { - "id": "element-60c9ac53-47a7-4ec2-a975-9d10bbe038bb", - "position": { - "left": 855.5, - "top": 26, - "width": 407, - "height": 140.250363384436, - "angle": 0 - }, - "expression": "\nshape \"square\" fill=\"#FFFFFF\" border=\"rgba(255,255,255,0)\" borderWidth=0 maintainAspect=false\n| render\n" - }, - { - "id": "element-f573cae3-0d2b-4265-9a6f-314c865c4e5c", - "position": { - "left": 423.5251242870414, - "top": 26, - "width": 407, - "height": 140.250363384436, - "angle": 0 - }, - "expression": "\nshape \"square\" fill=\"#FFFFFF\" border=\"rgba(255,255,255,0)\" borderWidth=0 maintainAspect=false\n| render\n" - }, - { - "id": "element-0f5b4a1f-8107-4f84-a12f-755b41dd1ca9", - "position": { - "left": 1035, - "top": 50.5, - "width": 197, - "height": 99, - "angle": 0 - }, - "expression": "\nkibana\n| selectFilter\n| essql\nquery=\"SELECT COUNT(DISTINCT OriginAirportID) as total_airports\nFROM kibana_sample_data_flights\"\n| math \"total_airports\"\n| metric \"AIRPORTS\"\nmetricFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=60 align=\"right\" color=\"#43988F\" weight=\"normal\" underline=false italic=false}\nlabelFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=24 align=\"right\" color=\"#43988F\" weight=\"normal\" underline=false italic=false}\n| render\n" - }, - { - "id": "element-096136f5-279d-4d29-a784-1bd33243db29", - "position": { - "left": 443.5, - "top": 275.250363384436, - "width": 131, - "height": 33, - "angle": 0 - }, - "expression": "\nkibana\n| selectFilter\n| demodata\n| markdown \"### LONGEST FLIGHT\"\nfont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=10 align=\"left\" color=\"#FFFFFF\" weight=\"normal\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-3d1e17df-6310-4968-8323-f0bcabf593e3", - "position": { - "left": 1042.5, - "top": 211.84415880749052, - "width": 191, - "height": 92.812409153891, - "angle": 0 - }, - "expression": "\nkibana\n| selectFilter\n| essql\nquery=\"SELECT MAX(DistanceMiles) as max_distance\nFROM kibana_sample_data_flights\nWHERE DistanceMiles > 0\"\n| math \"max_distance\"\n| formatNumber \"00.0a\"\n| metric \"MILES\"\nmetricFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=60 align=\"right\" color=\"#EFB341\" weight=\"normal\" underline=false italic=false}\nlabelFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=24 align=\"right\" color=\"#EFB341\" weight=\"normal\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-3604ef7b-8f10-4a5f-a4b9-f123e32960dd", - "position": { - "left": 1036, - "top": -51, - "width": 246, - "height": 50, - "angle": 0 - }, - "expression": "\ntimefilterControl compact=true column=\"timestamp\"\n| render\n", - "filter": "timefilter from=\"now-24h\" to=now column=timestamp" - }, - { - "id": "element-08333e7c-2986-4ee7-b07c-8c07be09c751", - "position": { - "left": -1, - "top": 386.250363384436, - "width": 1283, - "height": 358, - "angle": 0 - }, - "expression": "\nimage mode=\"cover\" dataurl={asset \"asset-2da3aba1-6e0f-4a79-879e-0ab3cfa170d6\"}\n| render\n" - }, - { - "id": "element-90336033-8ca4-4bf5-ad20-94162aec28b6", - "position": { - "left": 26, - "top": 26, - "width": 375.5, - "height": 672.125545076654, - "angle": 0 - }, - "expression": "\nshape \"square\" fill=\"#FFFFFF\" border=\"rgba(255,255,255,0)\" borderWidth=0 maintainAspect=false\n| render\n" - }, - { - "id": "element-e0ff80ac-8372-421f-918e-fd9f257b2f32", - "position": { - "left": -27, - "top": -1, - "width": 462, - "height": 699.125545076654, - "angle": 0 - }, - "expression": "\nimage mode=\"contain\" dataurl={asset \"asset-9e41d208-ec45-472d-a118-e2e2c811291b\"}\n| render\n" - }, - { - "id": "element-0d68f8e7-dc04-4358-b459-127c42e274b4", - "position": { - "left": 26, - "top": 145, - "width": 375.5, - "height": 153.375181692218, - "angle": 0 - }, - "expression": "\nshape \"circle\" fill=\"rgba(255,255,255,0)\" border=\"#48A8E0\" borderWidth=2 maintainAspect=true\n| render\n" - }, - { - "id": "element-f56821a4-4fd8-4de1-8bb7-6722c9ee5334", - "position": { - "left": 26, - "top": 56, - "width": 375.5, - "height": 44, - "angle": 0 - }, - "expression": "\nkibana\n| selectFilter\n| demodata\n| markdown \"TIME IN AIR\"\nfont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=36 align=\"center\" color=\"#48A8E0\" weight=\"normal\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-210b8adc-c5fa-412b-b280-146863fc9230", - "position": { - "left": 26, - "top": 330.562772538327, - "width": 375.5, - "height": 153.375181692218, - "angle": 0 - }, - "expression": "\nshape \"circle\" fill=\"rgba(255,255,255,0)\" border=\"#48A8E0\" borderWidth=2 maintainAspect=true\n| render\n", - "filter": null - }, - { - "id": "element-002f28e5-a132-4cbb-b1cb-f13f1cdeaac2", - "position": { - "left": 26, - "top": 509.562772538327, - "width": 375.5, - "height": 153.375181692218, - "angle": 0 - }, - "expression": "\nshape \"circle\" fill=\"rgba(255,255,255,0)\" border=\"#48A8E0\" borderWidth=2 maintainAspect=true\n| render\n", - "filter": null - }, - { - "id": "element-8a1248fa-2c2e-4be4-9e2b-95198279144d", - "position": { - "left": 26, - "top": 366.062772538327, - "width": 375.5, - "height": 65, - "angle": 0 - }, - "expression": "\nkibana\n| selectFilter\n| essql\nquery=\"SELECT FLOOR((SUM(FlightTimeMin) % 1440) / 60) as total_hours\nFROM kibana_sample_data_flights\"\n| math \"total_hours\"\n| formatNumber \"00\"\n| metric\nmetricFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=48 align=\"center\" color=\"#48A8E0\" weight=\"normal\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-2b890a9d-2f4d-4c4d-8275-ac371190d486", - "position": { - "left": 173.25, - "top": 232.000363384436, - "width": 81, - "height": 39, - "angle": 0 - }, - "expression": "\nkibana\n| selectFilter\n| demodata\n| markdown \"DAYS\"\nfont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=24 align=\"center\" color=\"#48A8E0\" weight=\"normal\" underline=false italic=false}\n| render\n" - }, - { - "id": "element-b253908c-b8bd-4ea1-a35f-eae5eb1ae63a", - "position": { - "left": 26, - "top": 179.750363384436, - "width": 375.5, - "height": 65, - "angle": 0 - }, - "expression": "\nkibana\n| selectFilter\n| essql\nquery=\"SELECT FLOOR(SUM(FlightTimeMin)/1440) as total_days\nFROM kibana_sample_data_flights\"\n| math \"total_days\"\n| formatNumber \"00a\"\n| metric\nmetricFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=48 align=\"center\" color=\"#48A8E0\" weight=\"normal\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-9fb07d44-c181-4d03-a431-492d1977a79c", - "position": { - "left": 26, - "top": 550.750363384436, - "width": 375.5, - "height": 54, - "angle": 0 - }, - "expression": "\nkibana\n| selectFilter\n| essql\nquery=\"SELECT ((SUM(FlightTimeMin) % 1440) / 60 ) as total_minutes\nFROM kibana_sample_data_flights\"\n| math \"total_minutes\"\n| formatNumber \"00\"\n| metric\nmetricFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=48 align=\"center\" color=\"#48A8E0\" weight=\"normal\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-60733afe-abce-4449-b0dd-5310d8ffffce", - "position": { - "left": 163.75, - "top": 416.9379542305451, - "width": 100, - "height": 39, - "angle": 0 - }, - "expression": "\nkibana\n| selectFilter\n| demodata\n| markdown \"HOURS\"\nfont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=24 align=\"center\" color=\"#48A8E0\" weight=\"normal\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-b7f3b9e3-2cb2-49e7-8768-b41afe0b49b1", - "position": { - "left": 173.25, - "top": 599.750363384436, - "width": 81, - "height": 39, - "angle": 0 - }, - "expression": "\nkibana\n| selectFilter\n| demodata\n| markdown \"MINS\"\nfont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=24 align=\"center\" color=\"#48A8E0\" weight=\"normal\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-526d29d1-8d5e-4c15-a5c2-fa23edf580ac", - "position": { - "left": 423.5251242870414, - "top": 185.312409153891, - "width": 407, - "height": 140.250363384436, - "angle": 0 - }, - "expression": "\nshape \"square\" fill=\"#FFFFFF\" border=\"rgba(255,255,255,0)\" borderWidth=0 maintainAspect=false\n| render\n" - }, - { - "id": "element-1d11f5a4-95a3-4a50-9191-6beb61bb8fbc", - "position": { - "left": 855.5, - "top": 343.687590846109, - "width": 407, - "height": 140.250363384436, - "angle": 0 - }, - "expression": "\nshape \"square\" fill=\"#FFFFFF\" border=\"rgba(255,255,255,0)\" borderWidth=0 maintainAspect=false\n| render\n" - }, - { - "id": "element-cfe6524e-b201-439a-974c-037406b760f6", - "position": { - "left": 616.5251242870414, - "top": 50.5, - "width": 187, - "height": 99, - "angle": 0 - }, - "expression": "\nkibana\n| selectFilter\n| essql query=\"SELECT COUNT(*) as total_flights\nFROM kibana_sample_data_flights\"\n| math \"total_flights\"\n| formatNumber \"0a]\"\n| metric \"FLIGHTS\"\nmetricFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=60 align=\"right\" color=\"#4184A5\" weight=\"normal\" underline=false italic=false}\nlabelFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=24 align=\"right\" color=\"#4184A5\" weight=\"normal\" underline=false italic=false}\n| render\n" - }, - { - "id": "element-e162c0b4-9393-43f9-8324-0263f40b4b85", - "position": { - "left": 439.52512428704136, - "top": 40.687590846109, - "width": 78.47487571295858, - "height": 59.312409153891, - "angle": 0 - }, - "expression": "\nimage mode=\"contain\" dataurl={asset \"asset-520a03a1-f522-4a18-ad4a-b84e87e4dc44\"}\n| render\n" - }, - { - "id": "element-0c37705e-004f-49c6-abda-9847b762c9f2", - "position": { - "left": 603.5251242870414, - "top": 211.84415880749054, - "width": 200, - "height": 100, - "angle": 0 - }, - "expression": "\nkibana\n| selectFilter\n| essql\nquery=\"SELECT MIN(DistanceMiles) as min_distance\nFROM kibana_sample_data_flights\nWHERE DistanceMiles > 0\"\n| math \"min_distance\"\n| formatNumber \"00.0a\"\n| metric \"MILES\"\nmetricFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=60 align=\"right\" color=\"#7EA030\" weight=\"normal\" underline=false italic=false}\nlabelFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=24 align=\"right\" color=\"#7EA030\" weight=\"normal\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-dd0f8a2e-1142-4140-bedb-913cae044204", - "position": { - "left": 439.52512428704136, - "top": 205.68795423054502, - "width": 78.47487571295858, - "height": 45.812409153891, - "angle": 0 - }, - "expression": "\nimage mode=\"contain\" dataurl={asset \"asset-08aa2e8f-6c3b-428f-82de-581004292cf0\"}\n| render\n" - }, - { - "id": "element-a81c83c7-8b61-4a1f-8da5-3270821c0089", - "position": { - "left": 435, - "top": 267.875181692218, - "width": 237, - "height": 30.5, - "angle": 0 - }, - "expression": "\nkibana\n| selectFilter\n| demodata\n| markdown \"SHORTEST FLIGHT\"\nfont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=24 align=\"left\" color=\"#7EA030\" weight=\"normal\" underline=false italic=false}\n| render\n" - }, - { - "id": "element-79393fba-8da9-4884-a280-e2a87e163f1a", - "position": { - "left": 870.7876864305622, - "top": 40.687590846109, - "width": 78.47487571295858, - "height": 59.312409153891, - "angle": 0 - }, - "expression": "\nimage mode=\"contain\" dataurl={asset \"asset-11a8022c-c1ac-4bbd-857a-db95fb8ca452\"}\n| render\n" - }, - { - "id": "element-ec914936-fc84-4915-82f7-1ca6b37ddc03", - "position": { - "left": 870.7876864305622, - "top": 262.500363384436, - "width": 237, - "height": 30.5, - "angle": 0 - }, - "expression": "\nkibana\n| selectFilter\n| demodata\n| markdown \"LONGEST FLIGHT\"\nfont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=24 align=\"left\" color=\"#EFB341\" weight=\"normal\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-8aae2d27-7a8c-4851-9b9c-a66e5b1dda9f", - "position": { - "left": 870.7876864305622, - "top": 198.93795423054502, - "width": 78.47487571295858, - "height": 59.312409153891, - "angle": 0 - }, - "expression": "\nimage mode=\"contain\" dataurl={asset \"asset-e73f53c5-fdcc-4232-bd6f-85a06281cf6c\"}\n| render\n", - "filter": null - }, - { - "id": "element-bca09809-6f73-4363-9732-5c86bb28f2f9", - "position": { - "left": 423.5251242870414, - "top": 343.687590846109, - "width": 407, - "height": 140.250363384436, - "angle": 0 - }, - "expression": "\nshape \"square\" fill=\"#FFFFFF\" border=\"rgba(255,255,255,0)\" borderWidth=0 maintainAspect=false\n| render\n" - }, - { - "id": "element-b6e7d5e1-3221-4274-ac97-5e0387d090c0", - "position": { - "left": 1033.5, - "top": 365.46897711527254, - "width": 200, - "height": 96.687590846109, - "angle": 0 - }, - "expression": "\nkibana\n| selectFilter\n| essql\nquery=\"SELECT COUNT(*) as total_cancellations\nFROM kibana_sample_data_flights\nWHERE Cancelled = true\"\n| math \"total_cancellations\"\n| formatNumber \"0a\"\n| metric \"CANCELLATIONS\"\nmetricFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=60 align=\"right\" color=\"#D88734\" weight=\"normal\" underline=false italic=false}\nlabelFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=24 align=\"right\" color=\"#D88734\" weight=\"normal\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-7ac04e37-e7aa-42cf-9afe-314ebd6de9d6", - "position": { - "left": 870.7876864305622, - "top": 364.4379542305451, - "width": 78.47487571295858, - "height": 59.312409153891, - "angle": 0 - }, - "expression": "\nimage mode=\"contain\" dataurl={asset \"asset-63a49130-fb96-4576-ac31-d86c934234d1\"}\n| render\n", - "filter": null - }, - { - "id": "element-0cf4194e-d460-40a1-a023-775f1946eb16", - "position": { - "left": 600.5251242870414, - "top": 364.4379542305451, - "width": 206, - "height": 105, - "angle": 0 - }, - "expression": "\nkibana\n| selectFilter\n| essql\nquery=\"SELECT COUNT(DISTINCT OriginCountry) as total_countries\nFROM kibana_sample_data_flights\"\n| math \"total_countries\"\n| metric \"COUNTRIES\"\nmetricFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=60 align=\"right\" color=\"#CB3072\" weight=\"normal\" underline=false italic=false}\nlabelFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=24 align=\"right\" color=\"#CB3072\" weight=\"normal\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-41734b58-e7aa-4888-980c-677420b1c736", - "position": { - "left": 439.52512428704136, - "top": 362.06277253832695, - "width": 78.47487571295858, - "height": 59.312409153891, - "angle": 0 - }, - "expression": "\nimage mode=\"contain\" dataurl={asset \"asset-4843e3bb-6cb0-43b7-b076-deea9a901fc6\"}\n| render\n", - "filter": null - } - ] - } - ], - "colors": [ - "#37988d", - "#c19628", - "#b83c6f", - "#3f9939", - "#1785b0", - "#ca5f35", - "#45bdb0", - "#f2bc33", - "#e74b8b", - "#4fbf48", - "#1ea6dc", - "#fd7643", - "#72cec3", - "#f5cc5d", - "#ec77a8", - "#7acf74", - "#4cbce4", - "#fd986f", - "#a1ded7", - "#f8dd91", - "#f2a4c5", - "#a6dfa2", - "#86d2ed", - "#fdba9f", - "#000000", - "#444444", - "#777777", - "#BBBBBB", - "rgba(255,255,255,0)" - ], - "@timestamp": "2018-10-31T17:32:39.068Z", - "@created": "2018-10-31T17:25:59.027Z", - "assets": { - "asset-2da3aba1-6e0f-4a79-879e-0ab3cfa170d6": { - "id": "asset-2da3aba1-6e0f-4a79-879e-0ab3cfa170d6", - "@created": "2018-10-13T16:17:38.860Z", - "type": "dataurl", - "value": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTUyNC4wOCAzNTUuMTkiPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDpub25lO30uY2xzLTJ7Y2xpcC1wYXRoOnVybCgjY2xpcC1wYXRoKTt9LmNscy0xMCwuY2xzLTMsLmNscy00LC5jbHMtNSwuY2xzLTYsLmNscy03e3N0cm9rZTojYWRlZmZmO30uY2xzLTEwLC5jbHMtMywuY2xzLTQsLmNscy01LC5jbHMtNiwuY2xzLTcsLmNscy04LC5jbHMtOXtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9LmNscy0ze2ZpbGw6dXJsKCNsaW5lYXItZ3JhZGllbnQpO30uY2xzLTR7ZmlsbDp1cmwoI2xpbmVhci1ncmFkaWVudC0yKTt9LmNscy01e2ZpbGw6dXJsKCNsaW5lYXItZ3JhZGllbnQtMyk7fS5jbHMtNntmaWxsOiMzMWM4ZmE7fS5jbHMtNywuY2xzLTl7ZmlsbDojMDBhOWU1O30uY2xzLTEwLC5jbHMtOHtmaWxsOiNmZmY7fS5jbHMtOCwuY2xzLTl7c3Ryb2tlOiMzMWM4ZmE7fTwvc3R5bGU+PGNsaXBQYXRoIGlkPSJjbGlwLXBhdGgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgLTI2NC43KSI+PHJlY3QgY2xhc3M9ImNscy0xIiB4PSIxMjEuODciIHdpZHRoPSIxMzIyLjc4IiBoZWlnaHQ9IjU5NS45Ii8+PC9jbGlwUGF0aD48bGluZWFyR3JhZGllbnQgaWQ9ImxpbmVhci1ncmFkaWVudCIgeDE9IjcyNS43MSIgeTE9IjExMS42NSIgeDI9IjcyNS43MSIgeTI9IjQ3OS4xMSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2FkZWZmZiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzMxYzhmYSIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJsaW5lYXItZ3JhZGllbnQtMiIgeDE9Ijc2Mi4wNCIgeTE9IjMwMy43NyIgeDI9Ijc2Mi4wNCIgeTI9IjkwNS4zMSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzMxYzhmYSIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzAwNzhhMCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJsaW5lYXItZ3JhZGllbnQtMyIgeDE9IjkzNS4zIiB5MT0iMzA0LjUiIHgyPSI5MzUuMyIgeTI9IjU3OS42NiIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2FkZWZmZiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzAwYTllNSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjx0aXRsZT5BaXJwb3J0IElsbHVzdHJhdGlvbjwvdGl0bGU+PGcgaWQ9IkxheWVyXzIiIGRhdGEtbmFtZT0iTGF5ZXIgMiI+PGcgaWQ9IkxheWVyXzQiIGRhdGEtbmFtZT0iTGF5ZXIgNCI+PGcgY2xhc3M9ImNscy0yIj48cmVjdCBjbGFzcz0iY2xzLTMiIHg9IjMuMzciIHk9IjE2NS40OSIgd2lkdGg9IjE0NDQuNjkiIGhlaWdodD0iMTg5LjIiLz48cGF0aCBjbGFzcz0iY2xzLTQiIGQ9Ik0xLjA5LDM4NnM3Ny44OS04MS42NSw5NS4wNi03Niw0NC44OSw5LDcwLDEzLjk0LDM4LjI5LDE2LjQxLDU2Ljc4LDE3LjI3LDIzLjc3LTM2Ljg0LDYyLjA2LTMxLjIxLDU2Ljc4LTEuODIsOTEuMTEtLjU3LDU5LjQyLDI0LjM4LDEwNS42NCwyNC4zOFM2MjMsMjk3LjM5LDY3MS44NiwyOTcuMzlzMTgzLjU0LDM0LjcsMjA3LjMxLDM0LjcsNDMuNzktNDQuNjIsNjIuMTctNDQuNjJTOTg4LjQ2LDMwNCwxMDAyLjQ3LDMwNHMxMDMuNDEtMzUuNDEsMTIzLjQyLTM3LjU0LDU2LjEzLTEuMTcsODYuOTIsMCw0MCwyMSw2My44MSwxOS4zNiw0Mi4zMi0xMiw2My40MS0xMS43NywxMjQuMDktMi44MywxNDYuNTQtMy44LDM3LDE4Ljg3LDM3LDE4Ljg3VjUxNlMxMzQuNDQsNDkyLjQyLDk4Ljc5LDUwNHMtNjMuMzgsNy40Mi03Ny45MSwxMlMuNSw1MjAuNTEuNSw1MjAuNTFaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIC0yNjQuNykiLz48cGF0aCBjbGFzcz0iY2xzLTUiIGQ9Ik02MjcuMDcsNDQ4LjE1bDMyLjEsODMuNDVoNTUwLjQ4bDMxLjMtNzMsNy42LThjLTM3LjE3LTEzLjgtNzUuNCw0LjI1LTEzNC44NiwxNC44N1M5OTUuODIsNDA4LjEyLDkwNC41LDM5NC4zMXMtMTU2LjA5LDMyLjkyLTIwMS43NSw0My41NC04MC43LDAtODAuNywwWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtMjY0LjcpIi8+PHBvbHlnb24gY2xhc3M9ImNscy02IiBwb2ludHM9IjUxOS41NyAyNTcuMDEgNDM0LjU3IDI1Ny4wMSA0MzQuNTcgMTQxLjk4IDUxOS41NyAxMzguNjYgNTE5LjU3IDI1Ny4wMSIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtNyIgcG9pbnRzPSI1MTkuNTcgMjU3LjAxIDYzOS4yNSAyNTcuMDEgNjM5LjI1IDE0MS45OCA1MTkuNTcgMTM4LjY2IDUxOS41NyAyNTcuMDEiLz48cG9seWdvbiBjbGFzcz0iY2xzLTYiIHBvaW50cz0iMTQzMy41OSAxMTIuOTUgMTQwOC4zNSAxMTIuOTUgMTQwMy42NiA4OC40MyAxNDMzLjU5IDg1Ljk4IDE0MzMuNTkgMTEyLjk1Ii8+PHBvbHlnb24gY2xhc3M9ImNscy03IiBwb2ludHM9IjE0MzMuNTkgMTEyLjk1IDE0NDMuNDcgMTEyLjk1IDE0NDguMDYgODguNDMgMTQzMy41OSA4NS45OCAxNDMzLjU5IDExMi45NSIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtNiIgcG9pbnRzPSIxMzk2Ljk1IDI3NS44MyAxMjI3LjY3IDI3NS44MyAxMjI3LjY3IDE2MC4wMiAxMzk2Ljk1IDE1NS40MiAxMzk2Ljk1IDI3NS44MyIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtNyIgcG9pbnRzPSIxMzk3LjIgMjc1LjgzIDE0MTYuNCAyNzUuODMgMTQxNi40IDE2MC4wMiAxMzk3LjIgMTU1LjQyIDEzOTcuMiAyNzUuODMiLz48cG9seWdvbiBjbGFzcz0iY2xzLTciIHBvaW50cz0iMTQzNC42OCAyODIuNDIgMTQxMS45NiAyODIuNDIgMTQxMS45NiAxMzEuNDYgMTQzNC42OCAxMzAuNjIgMTQzNC42OCAyODIuNDIiLz48cG9seWdvbiBjbGFzcz0iY2xzLTciIHBvaW50cz0iMTQ0Ny44NyAyNzkuNzUgMTQzMy44NSAyODIuNDIgMTQzMy44NSAxMzAuNiAxNDQ3Ljg3IDEzMS40NiAxNDQ3Ljg3IDI3OS43NSIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtNiIgcG9pbnRzPSIxNDMzLjg1IDEzOC40MyAxMzk4Ljk1IDEzOS4yNSAxMzk4Ljk1IDEwOC44OSAxNDMzLjg1IDEwOC4wNyAxNDMzLjg1IDEzOC40MyIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtNyIgcG9pbnRzPSIxNDUzLjc4IDEzOC45NiAxNDMzLjg1IDEzOC4yNSAxNDMzLjg1IDEwOC4wNyAxNDUzLjc4IDEwOC42MiAxNDUzLjc4IDEzOC45NiIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtNiIgcG9pbnRzPSIxMzc2LjcxIDI4Mi40MiAxMjEzLjA5IDI4Mi40MiAxMjEzLjA5IDE5MC42OSAxMzc2LjcxIDE4OC4wNSAxMzc2LjcxIDI4Mi40MiIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtNyIgcG9pbnRzPSIxMzc2LjcxIDI4Mi40MiAxMzg5Ljg2IDI3OS41MiAxMzg5Ljg2IDE4Ny43OSAxMzc2LjcxIDE4OC4wNSAxMzc2LjcxIDI4Mi40MiIvPjxlbGxpcHNlIGNsYXNzPSJjbHMtNyIgY3g9IjEyNDEuMjQiIGN5PSIxNTAuMjciIHJ4PSI5LjczIiByeT0iOS40NiIvPjxwYXRoIGNsYXNzPSJjbHMtNiIgZD0iTTYzMC41MSw1NDQuMjZINDI4LjE3VjQ3MEg2MzYuNmMyMC41MSwwLDk3LjQxLDI2Ljc5LDg5LjUsNTAuNTNTNjMwLjUxLDU0NC4yNiw2MzAuNTEsNTQ0LjI2WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtMjY0LjcpIi8+PGxpbmUgY2xhc3M9ImNscy04IiB4MT0iNjQyLjA4IiB5MT0iMjg2LjM1IiB4Mj0iNjQyLjA4IiB5Mj0iMzAzLjg0Ii8+PHJlY3QgY2xhc3M9ImNscy03IiB4PSI2MzQuNzciIHk9IjI2OC42IiB3aWR0aD0iMTQuNjEiIGhlaWdodD0iMjAuNyIgcng9IjQuMTMiIHJ5PSI0LjEzIi8+PGNpcmNsZSBjbGFzcz0iY2xzLTkiIGN4PSI2NDEuNDkiIGN5PSIzMTIuODgiIHI9IjEyLjUyIi8+PHBhdGggY2xhc3M9ImNscy03IiBkPSJNNDYwLjQ2LDU1My4yOGw1NS4zMywxM2MzLjI5Ljc4LDYuMzgtMi4xLDYuMzgtNS45NFY1MzJjMC0zLjc0LTIuOTQtNi42LTYuMTgtNmwtNTUuMjQsMTAuNGMtMy41NC42Ny02LjE4LDQuMTEtNi4yOSw4LjJoMEM0NTQuMzUsNTQ4Ljc4LDQ1Ni44OCw1NTIuNDQsNDYwLjQ2LDU1My4yOFoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgLTI2NC43KSIvPjxyZWN0IGNsYXNzPSJjbHMtNyIgeD0iNDIwLjQyIiB5PSIyNTAuNjkiIHdpZHRoPSI5MC4xOCIgaGVpZ2h0PSIxMi43OCIgcng9IjMuNjEiIHJ5PSIzLjYxIi8+PGxpbmUgY2xhc3M9ImNscy0xMCIgeDE9IjQ0OS41MSIgeTE9IjI2My40NyIgeDI9IjQ1OC4zMSIgeTI9IjI3Mi42OCIvPjxwYXRoIGNsYXNzPSJjbHMtNyIgZD0iTTcwNy40OCw0OTQuNzdoLTI1LjdBMjYuNTIsMjYuNTIsMCwwLDEsNjY2LDQ4OS41NGwtOC4yMy02LjExYTEuNzcsMS43NywwLDAsMSwxLTMuMTloMTguNjZTNzA5LjM3LDQ5NC43Nyw3MDcuNDgsNDk0Ljc3WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtMjY0LjcpIi8+PHJlY3QgY2xhc3M9ImNscy02IiB4PSI0NDEuMTIiIHk9IjIyNC41NCIgd2lkdGg9IjguMDciIGhlaWdodD0iMTIuNDEiIHJ4PSIzLjciIHJ5PSIzLjciLz48cmVjdCBjbGFzcz0iY2xzLTYiIHg9IjQ1OC42NiIgeT0iMjI0LjU0IiB3aWR0aD0iOC4wNyIgaGVpZ2h0PSIxMi40MSIgcng9IjMuNyIgcnk9IjMuNyIvPjxyZWN0IGNsYXNzPSJjbHMtNiIgeD0iNDc2LjIxIiB5PSIyMjQuNTQiIHdpZHRoPSI4LjA3IiBoZWlnaHQ9IjEyLjQxIiByeD0iMy43IiByeT0iMy43Ii8+PHJlY3QgY2xhc3M9ImNscy02IiB4PSI0OTMuNzUiIHk9IjIyNC41NCIgd2lkdGg9IjguMDciIGhlaWdodD0iMTIuNDEiIHJ4PSIzLjciIHJ5PSIzLjciLz48cmVjdCBjbGFzcz0iY2xzLTYiIHg9IjUxMS4yOSIgeT0iMjI0LjU0IiB3aWR0aD0iOC4wNyIgaGVpZ2h0PSIxMi40MSIgcng9IjMuNyIgcnk9IjMuNyIvPjxyZWN0IGNsYXNzPSJjbHMtNiIgeD0iNTI4Ljg0IiB5PSIyMjQuNTQiIHdpZHRoPSI4LjA3IiBoZWlnaHQ9IjEyLjQxIiByeD0iMy43IiByeT0iMy43Ii8+PHJlY3QgY2xhc3M9ImNscy02IiB4PSI1NDYuMzgiIHk9IjIyNC41NCIgd2lkdGg9IjguMDciIGhlaWdodD0iMTIuNDEiIHJ4PSIzLjciIHJ5PSIzLjciLz48cmVjdCBjbGFzcz0iY2xzLTYiIHg9IjU2My45MyIgeT0iMjI0LjU0IiB3aWR0aD0iOC4wNyIgaGVpZ2h0PSIxMi40MSIgcng9IjMuNyIgcnk9IjMuNyIvPjxyZWN0IGNsYXNzPSJjbHMtNiIgeD0iNTgxLjQ3IiB5PSIyMjQuNTQiIHdpZHRoPSI4LjA3IiBoZWlnaHQ9IjEyLjQxIiByeD0iMy43IiByeT0iMy43Ii8+PHJlY3QgY2xhc3M9ImNscy02IiB4PSI1OTkuMDIiIHk9IjIyNC41NCIgd2lkdGg9IjguMDciIGhlaWdodD0iMTIuNDEiIHJ4PSIzLjciIHJ5PSIzLjciLz48cmVjdCBjbGFzcz0iY2xzLTYiIHg9IjYxOS4zNSIgeT0iMjI0LjU0IiB3aWR0aD0iMTcuNDgiIGhlaWdodD0iMjYuNDQiLz48Y2lyY2xlIGNsYXNzPSJjbHMtNyIgY3g9IjEyMjUuNDYiIGN5PSIyODYuMjQiIHI9IjEzLjY5Ii8+PHBhdGggY2xhc3M9ImNscy03IiBkPSJNMTM1Mi4yOSw1NTEuODJIMTE3Ni45MVY1MjIuNjhsMTY5LjI3LDE4LjJhNi44NCw2Ljg0LDAsMCwxLDYuMTEsNi44WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtMjY0LjcpIi8+PGNpcmNsZSBjbGFzcz0iY2xzLTciIGN4PSIxMTAwLjE3IiBjeT0iMjg2LjI0IiByPSIxMy42OSIvPjxwYXRoIGNsYXNzPSJjbHMtNyIgZD0iTTk3My4zNCw1NTEuODJoMTc1LjM4VjUyMi42OGwtMTY5LjI3LDE4LjJhNi44NCw2Ljg0LDAsMCwwLTYuMTEsNi44WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtMjY0LjcpIi8+PHJlY3QgY2xhc3M9ImNscy02IiB4PSIxMTU5LjQ0IiB5PSIyMDguNTYiIHdpZHRoPSIxMy4wNyIgaGVpZ2h0PSI1NC41NSIgcng9IjEuOTEiIHJ5PSIxLjkxIi8+PGNpcmNsZSBjbGFzcz0iY2xzLTYiIGN4PSIxMTY1LjY5IiBjeT0iMjY3LjU0IiByPSIzMi4zOSIvPjxyZWN0IGNsYXNzPSJjbHMtNyIgeD0iMTEyNC44MiIgeT0iMjk5LjkyIiB3aWR0aD0iMTIuMjQiIGhlaWdodD0iMjEuMTkiIHJ4PSIxLjc5IiByeT0iMS43OSIvPjxsaW5lIGNsYXNzPSJjbHMtMTAiIHgxPSIxMTMwLjk0IiB5MT0iMjg3LjEyIiB4Mj0iMTEzMC45NCIgeTI9IjI5OS45MiIvPjxyZWN0IGNsYXNzPSJjbHMtNyIgeD0iMTE1OS44NSIgeT0iMzEyLjczIiB3aWR0aD0iMTIuMjQiIGhlaWdodD0iMjEuMTkiIHJ4PSIxLjc5IiByeT0iMS43OSIvPjxsaW5lIGNsYXNzPSJjbHMtMTAiIHgxPSIxMTY1Ljk3IiB5MT0iMjk5LjkyIiB4Mj0iMTE2NS45NyIgeTI9IjMxMi43MyIvPjxyZWN0IGNsYXNzPSJjbHMtNyIgeD0iMTE5OC4wOCIgeT0iMjk5LjE5IiB3aWR0aD0iMTIuMjQiIGhlaWdodD0iMjEuMTkiIHJ4PSIxLjc5IiByeT0iMS43OSIvPjxsaW5lIGNsYXNzPSJjbHMtMTAiIHgxPSIxMjA0LjIiIHkxPSIyODYuMzkiIHgyPSIxMjA0LjIiIHkyPSIyOTkuMTkiLz48Y2lyY2xlIGNsYXNzPSJjbHMtNyIgY3g9IjExNjUuNjkiIGN5PSIyNzYuODkiIHI9IjkuMzUiLz48cGF0aCBjbGFzcz0iY2xzLTciIGQ9Ik0xMTg1LDUyNi42M2gtMzguNWExLjc5LDEuNzksMCwwLDEtMS42Mi0yLjU1bDQtOC40MWExLjc4LDEuNzgsMCwwLDEsMS42Mi0xaDMxLjE2YTEuOCwxLjgsMCwwLDEsMS42NywxLjEzbDMuMzYsOC40MUExLjc5LDEuNzksMCwwLDEsMTE4NSw1MjYuNjNaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIC0yNjQuNykiLz48L2c+PC9nPjwvZz48L3N2Zz4=" - }, - "asset-9e41d208-ec45-472d-a118-e2e2c811291b": { - "id": "asset-9e41d208-ec45-472d-a118-e2e2c811291b", - "@created": "2018-10-13T16:33:38.197Z", - "type": "dataurl", - "value": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNDU5LjQ0IDI5NS45NSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOm5vbmU7fS5jbHMtMntjbGlwLXBhdGg6dXJsKCNjbGlwLXBhdGgpO30uY2xzLTN7b3BhY2l0eTowLjQ7fS5jbHMtNHtmaWxsOiNhZGVmZmY7fTwvc3R5bGU+PGNsaXBQYXRoIGlkPSJjbGlwLXBhdGgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgLTUzKSI+PHJlY3QgY2xhc3M9ImNscy0xIiB4PSIyOC4yIiB3aWR0aD0iNDY0LjQ5IiBoZWlnaHQ9IjM4OC4yNiIvPjwvY2xpcFBhdGg+PC9kZWZzPjx0aXRsZT5DbG91ZHM8L3RpdGxlPjxnIGlkPSJMYXllcl8yIiBkYXRhLW5hbWU9IkxheWVyIDIiPjxnIGlkPSJMYXllcl8xLTIiIGRhdGEtbmFtZT0iTGF5ZXIgMSI+PGcgY2xhc3M9ImNscy0yIj48ZyBjbGFzcz0iY2xzLTMiPjxwYXRoIGNsYXNzPSJjbHMtNCIgZD0iTTE3NS42NSwzMzUuN2ExNiwxNiwwLDAsMC02LDEuMTUsMjIuMSwyMi4xLDAsMCwwLTM2LjE3LTYuNiwzNC44NywzNC44NywwLDEsMC02OS40NCw2LjM1QTE2LDE2LDAsMCwwLDQzLDM0OUgxOTEuNDRBMTYsMTYsMCwwLDAsMTc1LjY1LDMzNS43WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtNTMpIi8+PC9nPjxnIGNsYXNzPSJjbHMtMyI+PHBhdGggY2xhc3M9ImNscy00IiBkPSJNMzM2LDE2N2ExMi4zNCwxMi4zNCwwLDAsMSw0LjU2Ljg4LDE2Ljg4LDE2Ljg4LDAsMCwxLDI3LjYxLTUsMjYuNjEsMjYuNjEsMCwwLDEsNDkuNjktMTEuMDksMjAuMzEsMjAuMzEsMCwwLDEsNC4xNi0uNDMsMjAuMDgsMjAuMDgsMCwwLDEsMTkuODMsMTYuOTMsMTIuMjUsMTIuMjUsMCwwLDEsMTcuNjIsMTAuMzlIMzIzLjc1QTEyLjIzLDEyLjIzLDAsMCwxLDMzNiwxNjdaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIC01MykiLz48L2c+PGcgY2xhc3M9ImNscy0zIj48cGF0aCBjbGFzcz0iY2xzLTQiIGQ9Ik0xNDAuNjQsOTMuNDVhMTMuNzYsMTMuNzYsMCwwLDAtNS4xMSwxLDE5LDE5LDAsMCwwLTE0LjE5LTExLjEzLDMzLjk0LDMzLjk0LDAsMCwwLTY3LjY4LDMuNjNjMCwuNDUsMCwuODksMCwxLjMzYTE4LjkyLDE4LjkyLDAsMCwwLTMxLjI1LDguNSwxMy43LDEzLjcsMCwwLDAtMjIuNDQsOEgxNTQuMTRBMTMuNzIsMTMuNzIsMCwwLDAsMTQwLjY0LDkzLjQ1WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtNTMpIi8+PC9nPjwvZz48L2c+PC9nPjwvc3ZnPg==" - }, - "asset-520a03a1-f522-4a18-ad4a-b84e87e4dc44": { - "id": "asset-520a03a1-f522-4a18-ad4a-b84e87e4dc44", - "@created": "2018-10-13T16:39:42.089Z", - "type": "dataurl", - "value": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1Ni4zMSA1Ni4zMSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7c3Ryb2tlOiMwMDc4YTA7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLXdpZHRoOjJweDt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPlBsYW5lIEljb248L3RpdGxlPjxnIGlkPSJMYXllcl8yIiBkYXRhLW5hbWU9IkxheWVyIDIiPjxnIGlkPSJMYXllcl8xLTIiIGRhdGEtbmFtZT0iTGF5ZXIgMSI+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNNDkuNTEsNDguOTMsNDEuMjYsMjIuNTIsNTMuNzYsMTBhNS4yOSw1LjI5LDAsMCwwLTcuNDgtNy40N2wtMTIuNSwxMi41TDcuMzgsNi43OUEuNy43LDAsMCwwLDYuNjksN0wxLjIsMTIuNDVhLjcuNywwLDAsMCwwLDFMMTkuODUsMjlsLTcuMjQsNy4yNC03Ljc0LS42YS43MS43MSwwLDAsMC0uNTMuMkwxLjIxLDM5YS42Ny42NywwLDAsMCwuMDgsMUw5LjQ1LDQ2bC4wNywwYy4xMS4xMy4yMi4yNi4zNC4zOHMuMjUuMjMuMzguMzRhLjM2LjM2LDAsMCwwLDAsLjA3TDE2LjMzLDU1YS42OC42OCwwLDAsMCwxLC4wN0wyMC40OSw1MmEuNjcuNjcsMCwwLDAsLjE5LS41NGwtLjU5LTcuNzQsNy4yNC03LjI0TDQyLjg1LDU1LjA2YS42OC42OCwwLDAsMCwxLDBsNS41LTUuNUEuNjYuNjYsMCwwLDAsNDkuNTEsNDguOTNaIi8+PC9nPjwvZz48L3N2Zz4=" - }, - "asset-08aa2e8f-6c3b-428f-82de-581004292cf0": { - "id": "asset-08aa2e8f-6c3b-428f-82de-581004292cf0", - "@created": "2018-10-13T16:42:03.959Z", - "type": "dataurl", - "value": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MC44MyAyMi4zNiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOm5vbmU7c3Ryb2tlOiM3NGEzMDA7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLXdpZHRoOjJweDt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPlNob3J0IGxlbmd0aCBJY29uPC90aXRsZT48ZyBpZD0iTGF5ZXJfMiIgZGF0YS1uYW1lPSJMYXllciAyIj48ZyBpZD0iTGF5ZXJfMS0yIiBkYXRhLW5hbWU9IkxheWVyIDEiPjxsaW5lIGNsYXNzPSJjbHMtMSIgeDE9IjEiIHgyPSIxIiB5Mj0iMjIuMzYiLz48bGluZSBjbGFzcz0iY2xzLTEiIHgxPSI0OS44MyIgeDI9IjQ5LjgzIiB5Mj0iMjIuMzYiLz48bGluZSBjbGFzcz0iY2xzLTEiIHgxPSI1LjY5IiB5MT0iMTEuMTgiIHgyPSIxMC4xNSIgeTI9IjExLjE4Ii8+PGxpbmUgY2xhc3M9ImNscy0xIiB4MT0iMTcuMTciIHkxPSIxMS4xOCIgeDI9IjIxLjYzIiB5Mj0iMTEuMTgiLz48bGluZSBjbGFzcz0iY2xzLTEiIHgxPSIyOC42NSIgeTE9IjExLjE4IiB4Mj0iMzMuMTEiIHkyPSIxMS4xOCIvPjxsaW5lIGNsYXNzPSJjbHMtMSIgeDE9IjQwLjEzIiB5MT0iMTEuMTgiIHgyPSI0NC41OSIgeTI9IjExLjE4Ii8+PC9nPjwvZz48L3N2Zz4=" - }, - "asset-11a8022c-c1ac-4bbd-857a-db95fb8ca452": { - "id": "asset-11a8022c-c1ac-4bbd-857a-db95fb8ca452", - "@created": "2018-10-13T16:44:44.648Z", - "type": "dataurl", - "value": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzOC4zOSA1Ny41NyI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7c3Ryb2tlOiMwMTliOGY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLXdpZHRoOjJweDt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPkxvY2F0aW9uIEljb248L3RpdGxlPjxnIGlkPSJMYXllcl8yIiBkYXRhLW5hbWU9IkxheWVyIDIiPjxnIGlkPSJMYXllcl8xLTIiIGRhdGEtbmFtZT0iTGF5ZXIgMSI+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNMTkuMTksMUExOC4xOSwxOC4xOSwwLDAsMCwyLjk0LDI3LjM2aDBhMTkuNTEsMTkuNTEsMCwwLDAsMSwxLjc4TDE5LjE5LDU1LjU3LDM0LjM4LDI5LjIxQTE4LjE5LDE4LjE5LDAsMCwwLDE5LjE5LDFabTAsMjMuMjlhNS41Myw1LjUzLDAsMSwxLDUuNTMtNS41M0E1LjUzLDUuNTMsMCwwLDEsMTkuMTksMjQuMjlaIi8+PC9nPjwvZz48L3N2Zz4=" - }, - "asset-e73f53c5-fdcc-4232-bd6f-85a06281cf6c": { - "id": "asset-e73f53c5-fdcc-4232-bd6f-85a06281cf6c", - "@created": "2018-10-13T16:49:36.056Z", - "type": "dataurl", - "value": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3Ny40NiAyMi4zNiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOm5vbmU7c3Ryb2tlOiNmOWIxMTA7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLXdpZHRoOjJweDt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPkxvbmcgbGVuZ3RoIEljb248L3RpdGxlPjxnIGlkPSJMYXllcl8yIiBkYXRhLW5hbWU9IkxheWVyIDIiPjxnIGlkPSJMYXllcl8xLTIiIGRhdGEtbmFtZT0iTGF5ZXIgMSI+PGxpbmUgY2xhc3M9ImNscy0xIiB4MT0iMSIgeDI9IjEiIHkyPSIyMi4zNiIvPjxsaW5lIGNsYXNzPSJjbHMtMSIgeDE9IjUuNjkiIHkxPSIxMS4xOCIgeDI9IjEwLjE1IiB5Mj0iMTEuMTgiLz48bGluZSBjbGFzcz0iY2xzLTEiIHgxPSIxNy4xNyIgeTE9IjExLjE4IiB4Mj0iMjEuNjMiIHkyPSIxMS4xOCIvPjxsaW5lIGNsYXNzPSJjbHMtMSIgeDE9IjI4LjY1IiB5MT0iMTEuMTgiIHgyPSIzMy4xMSIgeTI9IjExLjE4Ii8+PGxpbmUgY2xhc3M9ImNscy0xIiB4MT0iNDAuMTMiIHkxPSIxMS4xOCIgeDI9IjQ0LjU5IiB5Mj0iMTEuMTgiLz48bGluZSBjbGFzcz0iY2xzLTEiIHgxPSI1MS42MSIgeTE9IjExLjE4IiB4Mj0iNTYuMDgiIHkyPSIxMS4xOCIvPjxsaW5lIGNsYXNzPSJjbHMtMSIgeDE9IjYzLjA5IiB5MT0iMTEuMTgiIHgyPSI2Ny41NiIgeTI9IjExLjE4Ii8+PHBvbHlsaW5lIGNsYXNzPSJjbHMtMSIgcG9pbnRzPSI2OS41NyA1LjIyIDc2LjA1IDExLjcgNjkuNTcgMTguMTgiLz48L2c+PC9nPjwvc3ZnPg==" - }, - "asset-63a49130-fb96-4576-ac31-d86c934234d1": { - "id": "asset-63a49130-fb96-4576-ac31-d86c934234d1", - "@created": "2018-10-13T16:51:32.983Z", - "type": "dataurl", - "value": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMC43NiAzMC43NiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOm5vbmU7c3Ryb2tlOiNmZTk5MDA7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLXdpZHRoOjJweDt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPlggSWNvbjwvdGl0bGU+PGcgaWQ9IkxheWVyXzIiIGRhdGEtbmFtZT0iTGF5ZXIgMiI+PGcgaWQ9IkxheWVyXzEtMiIgZGF0YS1uYW1lPSJMYXllciAxIj48bGluZSBjbGFzcz0iY2xzLTEiIHgxPSIzMC4wNSIgeTE9IjAuNzEiIHgyPSIwLjcxIiB5Mj0iMzAuMDUiLz48bGluZSBjbGFzcz0iY2xzLTEiIHgxPSIwLjcxIiB5MT0iMC43MSIgeDI9IjMwLjA1IiB5Mj0iMzAuMDUiLz48L2c+PC9nPjwvc3ZnPg==" - }, - "asset-4843e3bb-6cb0-43b7-b076-deea9a901fc6": { - "id": "asset-4843e3bb-6cb0-43b7-b076-deea9a901fc6", - "@created": "2018-10-13T16:52:44.303Z", - "type": "dataurl", - "value": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NC42MiA1MS4wMyI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fS5jbHMtMSwuY2xzLTJ7c3Ryb2tlOiNmMzY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLXdpZHRoOjJweDt9LmNscy0ye2ZpbGw6bm9uZTt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPkZsYWcgSWNvbjwvdGl0bGU+PGcgaWQ9IkxheWVyXzIiIGRhdGEtbmFtZT0iTGF5ZXIgMiI+PGcgaWQ9IkxheWVyXzEtMiIgZGF0YS1uYW1lPSJMYXllciAxIj48cG9seWdvbiBjbGFzcz0iY2xzLTEiIHBvaW50cz0iNDIuOTMgMjguMTUgMSAyOC4xNSAxIDEgNDIuOTMgMSAzNS40NyAxNC41OCA0Mi45MyAyOC4xNSIvPjxsaW5lIGNsYXNzPSJjbHMtMiIgeDE9IjEiIHkxPSIxIiB4Mj0iMSIgeTI9IjUxLjAzIi8+PC9nPjwvZz48L3N2Zz4=" - } - } - } - } -] diff --git a/x-pack/plugins/canvas/server/sample_data/index.ts b/x-pack/plugins/canvas/server/sample_data/index.ts deleted file mode 100644 index 1cbb1d0aaee6a..0000000000000 --- a/x-pack/plugins/canvas/server/sample_data/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -// @ts-ignore this file is too large for TypeScript, so it is excluded from our project config -import ecommerceSavedObjects from './ecommerce_saved_objects.json'; -// @ts-ignore this file is too large for TypeScript, so it is excluded from our project config -import flightsSavedObjects from './flights_saved_objects.json'; -// @ts-ignore this file is too large for TypeScript, so it is excluded from our project config -import webLogsSavedObjects from './web_logs_saved_objects.json'; -import { loadSampleData } from './load_sample_data'; - -export { loadSampleData, ecommerceSavedObjects, flightsSavedObjects, webLogsSavedObjects }; diff --git a/x-pack/plugins/canvas/server/sample_data/load_sample_data.ts b/x-pack/plugins/canvas/server/sample_data/load_sample_data.ts deleted file mode 100644 index da219146a3609..0000000000000 --- a/x-pack/plugins/canvas/server/sample_data/load_sample_data.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 { SampleDataRegistrySetup } from '@kbn/home-plugin/server'; -import { CANVAS as label } from '../../i18n'; -import { ecommerceSavedObjects, flightsSavedObjects, webLogsSavedObjects } from '.'; - -export function loadSampleData( - addSavedObjectsToSampleDataset: SampleDataRegistrySetup['addSavedObjectsToSampleDataset'], - addAppLinksToSampleDataset: SampleDataRegistrySetup['addAppLinksToSampleDataset'] -) { - const now = new Date(); - const nowTimestamp = now.toISOString(); - - // @ts-expect-error: untyped local - function updateCanvasWorkpadTimestamps(savedObjects) { - // @ts-expect-error: untyped local - return savedObjects.map((savedObject) => { - if (savedObject.type === 'canvas-workpad') { - savedObject.attributes['@timestamp'] = nowTimestamp; - savedObject.attributes['@created'] = nowTimestamp; - } - - return savedObject; - }); - } - const getPath = (objectId: string) => `/app/canvas#/workpad/${objectId}`; - - addSavedObjectsToSampleDataset('ecommerce', updateCanvasWorkpadTimestamps(ecommerceSavedObjects)); - addAppLinksToSampleDataset('ecommerce', [ - { - sampleObject: { - type: 'canvas-workpad', - id: 'workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e', - }, - getPath, - icon: 'canvasApp', - label, - }, - ]); - - addSavedObjectsToSampleDataset('flights', updateCanvasWorkpadTimestamps(flightsSavedObjects)); - addAppLinksToSampleDataset('flights', [ - { - sampleObject: { - type: 'canvas-workpad', - id: 'workpad-a474e74b-aedc-47c3-894a-db77e62c41e0', - }, - getPath, - icon: 'canvasApp', - label, - }, - ]); - - addSavedObjectsToSampleDataset('logs', updateCanvasWorkpadTimestamps(webLogsSavedObjects)); - addAppLinksToSampleDataset('logs', [ - { - sampleObject: { - type: 'canvas-workpad', - id: 'workpad-ad72a4e9-b422-480c-be6d-a64a0b79541d', - }, - getPath, - icon: 'canvasApp', - label, - }, - ]); -} diff --git a/x-pack/plugins/canvas/server/sample_data/web_logs_saved_objects.json b/x-pack/plugins/canvas/server/sample_data/web_logs_saved_objects.json deleted file mode 100644 index 8c49935a4514d..0000000000000 --- a/x-pack/plugins/canvas/server/sample_data/web_logs_saved_objects.json +++ /dev/null @@ -1,757 +0,0 @@ -[ - { - "id": "workpad-ad72a4e9-b422-480c-be6d-a64a0b79541d", - "type": "canvas-workpad", - "updated_at": "2018-10-22T12:41:57.071Z", - "version": 1, - "migrationVersion": { - "canvas-workpad": "7.0.0" - }, - "attributes": { - "name": "[Logs] Web Traffic", - "width": 1280, - "height": 720, - "page": 0, - "pages": [ - { - "id": "page-e125ca0b-f6b2-437c-bc4c-918c468fbd9f", - "style": { - "background": "#000000" - }, - "transition": { - "name": "" - }, - "elements": [ - { - "id": "element-950e478d-39be-4630-9ebe-d46578951025", - "position": { - "left": 249, - "top": 574.3671875, - "width": 1013.5, - "height": 131.2578125, - "angle": 0 - }, - "expression": "shape \"square\" border=\"#CFD0D2\" borderWidth=2 maintainAspect=false\n| render\n" - }, - { - "id": "element-f6d67bd9-7edf-4a4c-944e-019eb2a89e46", - "position": { - "left": 249, - "top": 426.5, - "width": 1013.5, - "height": 131.2578125, - "angle": 0 - }, - "expression": "shape \"square\" border=\"#CFD0D2\" borderWidth=2 maintainAspect=false\n| render\n" - }, - { - "id": "element-fa296ebc-3ede-44f1-a027-f9aa0a8ba58b", - "position": { - "left": 249, - "top": 275.87109375, - "width": 1013.5, - "height": 131.2578125, - "angle": 0 - }, - "expression": "shape \"square\" border=\"#CFD0D2\" borderWidth=2 maintainAspect=false\n| render\n" - }, - { - "id": "element-21844047-2818-4071-bb9b-59cc68139c5f", - "position": { - "left": 589, - "top": 110.7578125, - "width": 318, - "height": 148.3046875, - "angle": 0 - }, - "expression": "shape \"square\" fill=\"#414143\" border=\"rgba(255,255,255,0)\" borderWidth=0 maintainAspect=false\n| render\n" - }, - { - "id": "element-a2136689-36d7-4f61-a9c7-5e4e3c89f2ca", - "position": { - "left": 924.5, - "top": 109.28515625, - "width": 318, - "height": 148.3046875, - "angle": 0 - }, - "expression": "shape \"square\" fill=\"#414143\" border=\"rgba(255,255,255,0)\" borderWidth=0 maintainAspect=false\n| render\n" - }, - { - "id": "element-2e02449b-433e-47c4-84ab-6f702619e21a", - "position": { - "left": 249, - "top": 109.6328125, - "width": 318, - "height": 148.3046875, - "angle": 0 - }, - "expression": "shape \"square\" fill=\"#414143\" border=\"rgba(255,255,255,0)\" borderWidth=0 maintainAspect=false\n| render\n" - }, - { - "id": "element-0f10bedf-728c-4207-96b8-bbb3021a91f1", - "position": { - "left": 245, - "top": 12, - "width": 1017.5, - "height": 65.90625, - "angle": 0 - }, - "expression": "shape \"square\" fill=\"#221F20\" border=\"#777777\" borderWidth=0 maintainAspect=false\n| render\n", - "filter": null - }, - { - "id": "element-4130544d-054a-4600-928a-39f1423788c6", - "position": { - "left": 13.5, - "top": 12, - "width": 211, - "height": 693.625, - "angle": 0 - }, - "expression": "shape \"square\" fill=\"#221F20\" border=\"#777777\" borderWidth=0 maintainAspect=false\n| render\n" - }, - { - "id": "element-57ffa8a7-f3f3-45bf-a35a-025a7647b8e7", - "position": { - "left": 19.25, - "top": 88.5625, - "width": 109, - "height": 7.25, - "angle": 0 - }, - "expression": "shape \"square\" fill=\"#CFD0D2\" border=\"#CFD0D2\" borderWidth=2 maintainAspect=false\n| render\n" - }, - { - "id": "element-4562db88-edbe-45b2-86aa-c549f2e25c98", - "position": { - "left": 671.5, - "top": 303.2421875, - "width": 574, - "height": 89.13671875, - "angle": 0 - }, - "expression": "shape \"square\" fill=\"#221F20\" border=\"rgba(255,255,255,0)\" borderWidth=0 maintainAspect=false\n| render\n" - }, - { - "id": "element-6556fa13-4557-47bc-bb9f-08a525604e13", - "position": { - "left": 56.25, - "top": 13.625, - "width": 168.25, - "height": 149, - "angle": 0 - }, - "expression": "shape \"circle\" fill=\"#221F20\" border=\"#CFD0D2\" borderWidth=2 maintainAspect=true\n| render\n" - }, - { - "id": "element-671589a9-54b6-46a1-b5e7-363b9d539795", - "position": { - "left": 258, - "top": 24.8125, - "width": 28, - "height": 36, - "angle": 0 - }, - "expression": "filters \n| essql\n query=\"SELECT host, response.keyword AS response\n FROM kibana_sample_data_logs\n WHERE host='artifacts.elastic.co'\n ORDER BY timestamp DESC\n LIMIT 1\"\n| alterColumn \"response\" type=\"number\" \n| getCell \"response\" \n| image mode=\"contain\" \n dataurl={\n asset {\n if {compare lt to=400} \n then=\"asset-0a807073-d056-4c7b-9bf4-225b71e47243\" \n else=\"asset-1343672d-7c02-4402-929e-0f8fef69cddd\"\n }\n } \n| render\n" - }, - { - "id": "element-d98c4bb0-f9ae-4e4a-838d-572b6919a3a2", - "position": { - "left": 20.375, - "top": 68, - "width": 60.25, - "height": 27.8125, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| demodata\n| markdown \"5XX\"\n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=18 align=\"left\" color=\"#CFD0D2\" weight=\"normal\" underline=false italic=false}\n| render\n" - }, - { - "id": "element-11de34da-7783-4d09-b22f-4ec1f8c957ea", - "position": { - "left": 573, - "top": 459.5, - "width": 79, - "height": 82, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| essql\n query=\"SELECT SUM(bytes) as total_bytes, host\n FROM kibana_sample_data_logs\n GROUP BY host\"\n| pointseries color=\"host\" size=\"total_bytes\"\n| pie hole=60 labels=false legend=false palette={palette \"#346822\" \"#57993F\" \"#C3F99C\" \"#6CBD38\" gradient=true}\n| render\n" - }, - { - "id": "element-a2e808f6-1b2e-4f84-931e-6c38424c5480", - "position": { - "left": 289.5, - "top": 29.4375, - "width": 165, - "height": 26.75, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| essql\n query=\"SELECT host\n FROM kibana_sample_data_logs\n WHERE host='artifacts.elastic.co'\n ORDER BY timestamp DESC\n LIMIT 1\"\n| markdown {getCell \"host\"}\n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=12 align=\"left\" color=\"#CFD0D2\" size=18 weight=\"lighter\" underline=false italic=false}\n| render\n" - }, - { - "id": "element-bc35a3fe-c896-4898-a7f3-e5778958d6b0", - "position": { - "left": 265, - "top": 436.25, - "width": 302, - "height": 110, - "angle": 0, - "parent": null - }, - "expression": "kibana\n| selectFilter\n| essql \n query=\"SELECT SUM(bytes) as total_bytes\n FROM kibana_sample_data_logs\"\n| math \"total_bytes\"\n| formatNumber \"0.00b\"\n| metric \"BYTES TRANSFERRED\"\n metricFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=48 align=\"left\" color=\"#FFFFFF\" weight=\"normal\" underline=false italic=false}\n labelFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=30 align=\"left\" color=\"#FFFFFF\" weight=\"lighter\" underline=false italic=false}\n| render\n" - }, - { - "id": "element-a684991f-f179-4fcc-b474-5ed71b0a6f3e", - "position": { - "left": 264.5, - "top": 586.5, - "width": 290.5, - "height": 104, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| essql \n query=\"SELECT COUNT(timestamp) as total_visitors\n FROM kibana_sample_data_logs\"\n| math \"total_visitors\"\n| metric \"TOTAL VISITORS\"\n metricFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=48 align=\"left\" color=\"#FFFFFF\" weight=\"normal\" underline=false italic=false}\n labelFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=30 align=\"left\" color=\"#FFFFFF\" weight=\"lighter\" underline=false italic=false}\n| render\n" - }, - { - "id": "element-a8a8fa93-8e08-4e9f-b3e4-f4014543fe1f", - "position": { - "left": 515.5, - "top": 29.4375, - "width": 147, - "height": 25.75, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| essql \n query=\"SELECT host\n FROM kibana_sample_data_logs\n WHERE host='www.elastic.co'\"\n| markdown {getCell \"host\"}\n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=12 align=\"left\" color=\"#CFD0D2\" size=18 weight=\"lighter\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-38b281b2-ab5e-41ee-aa3e-e48bc3f778df", - "position": { - "left": 719, - "top": 28.625, - "width": 247, - "height": 26.75, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| essql\n query=\"SELECT host \n FROM kibana_sample_data_logs\n WHERE host='cdn.elastic-elastic-elastic.org'\"\n| markdown {getCell \"host\"}\n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=12 align=\"left\" color=\"#CFD0D2\" size=18 weight=\"lighter\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-b481bf28-15d3-4f0b-b67b-e268c68bfe9c", - "position": { - "left": 1040.5, - "top": 28.8125, - "width": 209, - "height": 27.375, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| essql\n query=\"SELECT host\n FROM kibana_sample_data_logs\n WHERE host='elastic-elastic-elastic.org'\"\n| markdown {getCell \"host\"}\n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=12 align=\"left\" color=\"#CFD0D2\" size=18 weight=\"lighter\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-ebb18e7a-ec51-4072-8453-58fbb883584b", - "position": { - "left": 677.5, - "top": 451.77734375, - "width": 578, - "height": 90.5, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| essql\n query=\"SELECT SUM(bytes) as total_bytes, HOUR_OF_DAY(timestamp) as hour, host\n FROM kibana_sample_data_logs\n GROUP BY host, HOUR_OF_DAY(timestamp)\n ORDER BY HOUR_OF_DAY(timestamp) DESC\"\n| pointseries x=\"hour\" y=\"total_bytes\" color=\"host\"\n| plot defaultStyle={seriesStyle bars=0 lines=3 points=0} legend=false xaxis=false yaxis=false palette={palette \"#346822\" \"#57993F\" \"#C3F99C\" \"#6CBD38\" gradient=true}\n| render containerStyle={containerStyle backgroundColor=\"#221F20\"}\n" - }, - { - "id": "element-05a065b5-5f01-4502-9f91-fdfc3de1b456", - "position": { - "left": 677.5, - "top": 594.875, - "width": 578, - "height": 87.25, - "angle": 0, - "parent": null - }, - "expression": "kibana\n| selectFilter\n| essql\n query=\"SELECT COUNT(timestamp) as total_visitors, HOUR_OF_DAY(timestamp) as hour, host\n FROM kibana_sample_data_logs\n GROUP BY host, HOUR_OF_DAY(timestamp)\n ORDER BY HOUR_OF_DAY(timestamp) DESC\"\n| pointseries x=\"hour\" y=\"total_visitors\" color=\"host\"\n| plot defaultStyle={seriesStyle bars=0 lines=3 points=0} legend=false xaxis=false yaxis=false palette={palette \"#C83C5C\" \"#D56F79\" \"#F6C4C5\" \"#F1A3A6\" gradient=true}\n| render containerStyle={containerStyle backgroundColor=\"#221F20\"}\n" - }, - { - "id": "element-ce1da927-2d4f-413e-85e9-4fd1106b5207", - "position": { - "left": 573, - "top": 604.5, - "width": 79, - "height": 82, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| essql\n query=\"SELECT COUNT(timestamp) as total_visitors, host\n FROM kibana_sample_data_logs\n GROUP BY host\"\n| pointseries color=\"host\" size=\"total_visitors\"\n| pie hole=60 labels=false legend=false palette={palette \"#C83C5C\" \"#D56F79\" \"#F6C4C5\" \"#F1A3A6\" gradient=true}\n| render\n", - "filter": null - }, - { - "id": "element-acccadaf-3ce8-4ca6-8205-4529d42c1eef", - "position": { - "left": 677, - "top": 290.62109375, - "width": 562, - "height": 101.7578125, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| essql\nquery=\"SELECT COUNT(timestamp) as total_errors, timestamp\nFROM kibana_sample_data_logs\nWHERE tags LIKE '%warning%'\nGROUP BY timestamp\nORDER BY timestamp DESC\"\n| pointseries x=\"timestamp\" y=\"total_errors\"\n| plot defaultStyle={seriesStyle bars=\"1\" lines=\"0\" points=0 color=\"#E9782F\"} legend=false xaxis=false yaxis=false\n| render\n" - }, - { - "id": "element-1ae8ea70-993a-4503-916a-ce98717054f6", - "position": { - "left": 680.5, - "top": 290.62109375, - "width": 562, - "height": 101.7578125, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| essql\n query=\"SELECT COUNT(timestamp) as total_errors, timestamp\n FROM kibana_sample_data_logs\n WHERE tags LIKE '%error%'\n GROUP BY timestamp\n ORDER BY timestamp DESC\"\n| pointseries x=\"timestamp\" y=\"total_errors\"\n| plot defaultStyle={seriesStyle bars=\"1\" lines=\"0\" points=0 color=\"#F2C242\"} legend=false xaxis=false yaxis=false\n| render\n" - }, - { - "id": "element-70a2bca6-de31-4bee-81b1-b18528820645", - "position": { - "left": 264.5, - "top": 287.4375, - "width": 215, - "height": 104.94140625, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| essql\n query=\"SELECT COUNT(timestamp) as total_errors\n FROM kibana_sample_data_logs\n WHERE tags LIKE '%warning%' OR tags LIKE '%error%'\"\n| math \"total_errors\"\n| metric \"TOTAL ISSUES\"\n metricFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=48 align=\"left\" color=\"#FFFFFF\" weight=\"normal\" underline=false italic=false}\n labelFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=30 align=\"left\" color=\"#FFFFFF\" weight=\"lighter\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-5b36cc1b-e0d6-4ddc-bf16-849fc41e1f16", - "position": { - "left": 562.5, - "top": 294.1953125, - "width": 100, - "height": 98.18359375, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| essql\n query=\"SELECT COUNT(timestamp) as total_errors\n FROM kibana_sample_data_logs\n WHERE tags LIKE '%warning%' OR tags LIKE '%error%'\"\n| math \"clamp(sum(total_errors / 100),0,1)\"\n| revealImage origin=\"bottom\" image={asset \"asset-a1e77720-eb0b-42a0-a1c0-a159035e4f26\"} emptyImage={asset \"asset-d2cca77f-ba40-4acb-beff-38fab19c7b65\"}\n| render\n" - }, - { - "id": "element-9d5e9c4b-a7f5-459b-ab15-cfcd1d1d00c9", - "position": { - "left": 319.5, - "top": 120.17578125, - "width": 239, - "height": 39.68359375, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| demodata\n| markdown \"MACHINE\"\nfont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=30 align=\"left\" color=\"#FFFFFF\" weight=\"lighter\" underline=false italic=false}\n| render\n" - }, - { - "id": "element-d575ce4e-4aa5-4c54-993a-ca56890e434f", - "position": { - "left": 259.5, - "top": 171.69140625, - "width": 295.5, - "height": 80.12109375, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| esdocs index=\"kibana_sample_data_logs\" sort=\"timestamp, desc\" fields=\"machine.os, machine.ram\" count=1\n| markdown \"**OS:** \" {getCell \"machine.os\"} \"\\\n **RAM:** \" {getCell \"machine.ram\" | formatNumber \"00.0b\"}\n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=20 align=\"left\" color=\"#FFFFFF\" weight=\"lighter\" underline=false italic=false}\n| render containerStyle={containerStyle padding=\"5px\"}\n" - }, - { - "id": "element-6f777a39-f934-4263-94ea-4f1d48f3849c", - "position": { - "left": 604.75, - "top": 171.69140625, - "width": 298, - "height": 83.74609375, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| esdocs index=\"kibana_sample_data_logs\" sort=\"timestamp, desc\" fields=\"request\" count=1\n| markdown {getCell \"request\"}\n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=20 align=\"left\" color=\"#FFFFFF\" weight=\"lighter\" underline=false italic=false}\n| render containerStyle={containerStyle padding=\"5px\"}\n" - }, - { - "id": "element-7bfcdb0f-3533-4e50-a94a-f0487f374bff", - "position": { - "left": 941.5, - "top": 171.69140625, - "width": 284, - "height": 80.12109375, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| esdocs index=\"kibana_sample_data_logs\" sort=\"timestamp, desc\" fields=\"geo.src, geo.dest\" count=1\n| markdown \"**ORIGIN COUNTRY:** \" {getCell \"geo.src\"} \"\\\n **DESTINATION:** \" {getCell \"geo.dest\"}\n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=20 align=\"left\" color=\"#FFFFFF\" weight=\"lighter\" underline=false italic=false}\n| render containerStyle={containerStyle padding=\"5px\"}\n" - }, - { - "id": "element-db2d0540-f734-421e-a9a5-7f6e9eef0bd7", - "position": { - "left": 987.5, - "top": -51, - "width": 285, - "height": 50, - "angle": 0, - "parent": null - }, - "expression": "timefilterControl compact=true column=\"timestamp\"\n| render\n", - "filter": "timefilter from=\"now-24h\" to=now column=timestamp" - }, - { - "id": "element-a29b5f31-3204-4174-a511-5869648ccae0", - "position": { - "left": 73.75, - "top": 34.2578125, - "width": 133.25, - "height": 108.609375, - "angle": 0 - }, - "expression": "shape \"circle\" fill=\"#C83C5B\" border=\"rgba(255,255,255,0)\" borderWidth=0 maintainAspect=true\n| render\n" - }, - { - "id": "element-c509ce21-e729-4aa5-b892-f4be441cde26", - "position": { - "left": 94.875, - "top": 64, - "width": 91, - "height": 49.125, - "angle": 0 - }, - "expression": "filters \n| essql \n query=\"SELECT COUNT(*) as response_code\n FROM kibana_sample_data_logs\n WHERE response >= 500\"\n| math \"response_code\"\n| metric\n metricFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=48 align=\"center\" color=\"#FFFFFF\" weight=\"bold\" underline=false italic=false}\n| render\n" - }, - { - "id": "element-d27ad090-4e58-4d9c-aef7-ccb8ca043758", - "position": { - "left": 20.375, - "top": 612.625, - "width": 109, - "height": 7.25, - "angle": 0 - }, - "expression": "shape \"square\" fill=\"#CFD0D2\" border=\"#CFD0D2\" borderWidth=2 maintainAspect=false\n| render\n" - }, - { - "id": "element-99baa692-12dc-4d8d-9d78-05cb1aa7f7a8", - "position": { - "left": 21.1875, - "top": 436.25, - "width": 109, - "height": 7.25, - "angle": 0 - }, - "expression": "shape \"square\" fill=\"#CFD0D2\" border=\"#CFD0D2\" borderWidth=2 maintainAspect=false\n| render\n" - }, - { - "id": "element-f0412810-61fb-4ab8-ba9f-a69ba344d4d7", - "position": { - "left": 20.25, - "top": 251.8125, - "width": 109, - "height": 7.25, - "angle": 0 - }, - "expression": "shape \"square\" fill=\"#CFD0D2\" border=\"#CFD0D2\" borderWidth=2 maintainAspect=false\n| render\n" - }, - { - "id": "element-42587f38-4f6e-4a23-9ade-d0f23449efba", - "position": { - "left": 56.25, - "top": 183.4375, - "width": 168.25, - "height": 149, - "angle": 0 - }, - "expression": "shape \"circle\" fill=\"#221F20\" border=\"#CFD0D2\" borderWidth=2 maintainAspect=true\n| render\n" - }, - { - "id": "element-40d243ea-088c-4e17-b797-c47c8b94e282", - "position": { - "left": 56.25, - "top": 369, - "width": 168.25, - "height": 149, - "angle": 0 - }, - "expression": "shape \"circle\" fill=\"#221F20\" border=\"#CFD0D2\" borderWidth=2 maintainAspect=true\n| render\n" - }, - { - "id": "element-349aaa30-c164-4b95-b5aa-891a43e7b693", - "position": { - "left": 56.25, - "top": 541.5, - "width": 168.25, - "height": 149, - "angle": 0 - }, - "expression": "shape \"circle\" fill=\"#221F20\" border=\"#CFD0D2\" borderWidth=2 maintainAspect=true\n| render\n" - }, - { - "id": "element-e92c89d9-4ddd-499c-8367-94271da02b3d", - "position": { - "left": 73.75, - "top": 203.6328125, - "width": 133.25, - "height": 108.609375, - "angle": 0 - }, - "expression": "shape \"circle\" fill=\"#E9782F\" border=\"rgba(255,255,255,0)\" borderWidth=0 maintainAspect=true\n| render\n" - }, - { - "id": "element-f827824d-fc40-4905-937d-060e9748acce", - "position": { - "left": 74.75, - "top": 389.1953125, - "width": 133.25, - "height": 108.609375, - "angle": 0 - }, - "expression": "shape \"circle\" fill=\"#F2C242\" border=\"rgba(255,255,255,0)\" borderWidth=0 maintainAspect=true\n| render\n" - }, - { - "id": "element-f7e459a0-613e-43f4-9ced-d5f4ae6f3c47", - "position": { - "left": 74.75, - "top": 561.9453125, - "width": 133.25, - "height": 108.609375, - "angle": 0 - }, - "expression": "shape \"circle\" fill=\"#6CBD38\" border=\"rgba(255,255,255,0)\" borderWidth=0 maintainAspect=true\n| render\n" - }, - { - "id": "element-f5760f24-d9cb-42e8-ae60-13c60335f049", - "position": { - "left": 21.1875, - "top": 590.1875, - "width": 61.375, - "height": 25.8125, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| demodata\n| markdown \"2XX\"\n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=18 align=\"left\" color=\"#CFD0D2\" weight=\"normal\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-5e9998b3-7c27-4f4a-8f61-6b857339e07e", - "position": { - "left": 21.1875, - "top": 413.59375, - "width": 50.625, - "height": 26.28125, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| demodata\n| markdown \"3XX\"\n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=18 align=\"left\" color=\"#CFD0D2\" weight=\"normal\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-744e0e58-e1be-4919-a9fb-eaa46c5d8aaa", - "position": { - "left": 19.25, - "top": 228.4375, - "width": 54.5, - "height": 30.625, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| demodata\n| markdown \"4XX\"\n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=18 align=\"left\" color=\"#CFD0D2\" weight=\"normal\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-9f7bea99-8c0c-49b1-b8a3-9af26eb0466d", - "position": { - "left": 94.875, - "top": 228.4375, - "width": 91, - "height": 59, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| essql \n query=\"SELECT COUNT(*) as response_code\n FROM kibana_sample_data_logs\n WHERE response >= 400 AND response < 500\"\n| math \"unique(response_code)\"\n| metric\n metricFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=48 align=\"center\" color=\"#FFFFFF\" weight=\"bold\" underline=false italic=false}\n| render\n" - }, - { - "id": "element-607512ac-3d83-4d36-87c1-d5f13502f084", - "position": { - "left": 97.5, - "top": 418.25, - "width": 88.375, - "height": 59, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| essql\n query=\"SELECT COUNT(*) as response_code\n FROM kibana_sample_data_logs\n WHERE response >= 300 AND response < 400\"\n| math \"response_code\"\n| metric\n metricFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=48 align=\"center\" color=\"#FFFFFF\" weight=\"bold\" underline=false italic=false}\n| render\n" - }, - { - "id": "element-3cc56225-d739-4279-97f2-5c179fb013a5", - "position": { - "left": 94.875, - "top": 590.1875, - "width": 91, - "height": 59, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| essql\n query=\"SELECT COUNT(*) as response_code\n FROM kibana_sample_data_logs\n WHERE response >= 200 AND response < 300\"\n| math \"response_code\"\n| formatNumber \"0a\"\n| metric\n metricFont={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=48 align=\"center\" color=\"#FFFFFF\" weight=\"bold\" underline=false italic=false}\n| render\n" - }, - { - "id": "element-f7d5956f-287d-4ac6-bcfb-fca6f8ebe63e", - "position": { - "left": 479.5, - "top": 24, - "width": 28, - "height": 36, - "angle": 0 - }, - "expression": "filters \n| essql\n query=\"SELECT host, response.keyword AS response\n FROM kibana_sample_data_logs\n WHERE host='www.elastic.co'\n ORDER BY timestamp DESC\n LIMIT 1\"\n| alterColumn \"response\" type=\"number\" \n| image mode=\"contain\" \n dataurl={\n asset {\n if {getCell \"response\" | compare lt to=400} \n then=\"asset-0a807073-d056-4c7b-9bf4-225b71e47243\" \n else=\"asset-1343672d-7c02-4402-929e-0f8fef69cddd\"\n }\n } \n| render\n", - "filter": null - }, - { - "id": "element-254cc607-c924-49f4-a7f7-737480b4a056", - "position": { - "left": 682.5, - "top": 24.5, - "width": 28, - "height": 36, - "angle": 0 - }, - "expression": "filters \n| essql\n query=\"SELECT host, response.keyword AS response\n FROM kibana_sample_data_logs\n WHERE host='cdn.elastic-elastic-elastic.org'\n ORDER BY timestamp DESC\n LIMIT 1\"\n| alterColumn \"response\" type=\"number\" \n| image mode=\"contain\" \n dataurl={\n asset {\n if {getCell \"response\" | compare lt to=400} then=\"asset-0a807073-d056-4c7b-9bf4-225b71e47243\" else=\"asset-1343672d-7c02-4402-929e-0f8fef69cddd\"\n }\n } \n| render\n", - "filter": null - }, - { - "id": "element-96aa263d-8222-4adc-94ff-2277a1f55dff", - "position": { - "left": 1001.5, - "top": 24, - "width": 28, - "height": 36, - "angle": 0 - }, - "expression": "filters \n| essql\n query=\"SELECT host, response.keyword AS response\n FROM kibana_sample_data_logs\n WHERE host='elastic-elastic-elastic.org'\n ORDER BY timestamp DESC\n LIMIT 1\"\n| alterColumn \"response\" type=\"number\" \n| image mode=\"contain\" \n dataurl={\n asset {\n if {getCell \"response\" | compare lt to=400} then=\"asset-0a807073-d056-4c7b-9bf4-225b71e47243\" else=\"asset-1343672d-7c02-4402-929e-0f8fef69cddd\"\n }\n } \n| render\n", - "filter": null - }, - { - "id": "element-618ced11-d0da-4603-ae0b-77a5bb35e6c4", - "position": { - "left": 263.25, - "top": 128.658203125, - "width": 45, - "height": 28.25, - "angle": 0 - }, - "expression": "image mode=\"contain\" dataurl={asset \"asset-e9118351-dcc5-4a27-a7fc-f15eb308999b\"}\n| render\n" - }, - { - "id": "element-0f3c5e51-24b8-421e-b151-0c0229f80550", - "position": { - "left": 658.5, - "top": 119.525390625, - "width": 220, - "height": 37.3828125, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| demodata\n| markdown \"REQUEST\"\n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=30 align=\"left\" color=\"#FFFFFF\" weight=\"lighter\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-98d5f63a-ef33-415a-a1ec-44a5564c239f", - "position": { - "left": 996.5, - "top": 117.525390625, - "width": 220, - "height": 43.68359375, - "angle": 0 - }, - "expression": "kibana\n| selectFilter\n| demodata\n| markdown \"GEO\"\n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=30 align=\"left\" color=\"#FFFFFF\" weight=\"lighter\" underline=false italic=false}\n| render\n", - "filter": null - }, - { - "id": "element-88f2579a-50af-4330-baaf-e751092588a3", - "position": { - "left": 604.75, - "top": 129.30859375, - "width": 45, - "height": 28.25, - "angle": 0 - }, - "expression": "image mode=\"contain\" dataurl={asset \"asset-c2128a19-e5ba-450c-99c0-a68abbdfa684\"}\n| render\n", - "filter": null - }, - { - "id": "element-eed60428-2b80-4558-8bcc-40b516fa63d4", - "position": { - "left": 942.5, - "top": 124.7421875, - "width": 45, - "height": 29.25, - "angle": 0 - }, - "expression": "image mode=\"contain\" dataurl={asset \"asset-86622ebc-32db-40fa-8310-262c85a10979\"}\n| render\n", - "filter": null - } - ], - "groups": [] - } - ], - "colors": [ - "#37988d", - "#c19628", - "#b83c6f", - "#3f9939", - "#1785b0", - "#ca5f35", - "#45bdb0", - "#f2bc33", - "#e74b8b", - "#4fbf48", - "#1ea6dc", - "#fd7643", - "#72cec3", - "#f5cc5d", - "#ec77a8", - "#7acf74", - "#4cbce4", - "#fd986f", - "#a1ded7", - "#f8dd91", - "#f2a4c5", - "#a6dfa2", - "#86d2ed", - "#fdba9f", - "#000000", - "#444444", - "#777777", - "#BBBBBB", - "#FFFFFF", - "rgba(255,255,255,0)" - ], - "@timestamp": "2019-06-03T20:54:03.958Z", - "@created": "2019-06-03T20:46:16.137Z", - "assets": { - "asset-a1e77720-eb0b-42a0-a1c0-a159035e4f26": { - "id": "asset-a1e77720-eb0b-42a0-a1c0-a159035e4f26", - "@created": "2018-10-13T10:48:32.499Z", - "type": "dataurl", - "value": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MS41MyA4Ny4yOSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNkOTI4NTk7fS5jbHMtMntmaWxsOiNmOTcxMDA7fS5jbHMtM3tmaWxsOiNmOWMxMDA7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5GaXJlIENvbG9yPC90aXRsZT48ZyBpZD0iTGF5ZXJfMiIgZGF0YS1uYW1lPSJMYXllciAyIj48ZyBpZD0iTGF5ZXJfMi0yIiBkYXRhLW5hbWU9IkxheWVyIDIiPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTMwLjc3LDBoMFM2MS41MywyNy43MSw2MS41Myw1Ni41M2MwLDE5LjI3LTEzLjc3LDMwLjc3LTMwLjc3LDMwLjc3aDBBMzAuOCwzMC44LDAsMCwxLDAsNTYuNTNDMCwyOC42OCwzMC43NywwLDMwLjc3LDBaIi8+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNMzAuNzcsMjBoMHMyMy43LDIxLjM0LDIzLjcsNDMuNTRjMCwxNC44NC0xMC42MSwyMy43LTIzLjcsMjMuN2gwYTIzLjczLDIzLjczLDAsMCwxLTIzLjctMjMuN0M3LjA3LDQyLjE0LDMwLjc3LDIwLDMwLjc3LDIwWiIvPjxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTMwLjc3LDUyLjA3aDBTNDMuMTgsNjMuMjUsNDMuMTgsNzQuODhjMCw3Ljc3LTUuNTYsMTIuNDItMTIuNDIsMTIuNDJoMEExMi40MywxMi40MywwLDAsMSwxOC4zNSw3NC44OEMxOC4zNSw2My42NCwzMC43Nyw1Mi4wNywzMC43Nyw1Mi4wN1oiLz48L2c+PC9nPjwvc3ZnPg==" - }, - "asset-d2cca77f-ba40-4acb-beff-38fab19c7b65": { - "id": "asset-d2cca77f-ba40-4acb-beff-38fab19c7b65", - "@created": "2018-10-13T10:48:37.615Z", - "type": "dataurl", - "value": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MS41MyA4Ny4yOSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiM2ZDZlNzE7fS5jbHMtMntmaWxsOiM5Mzk1OTg7fS5jbHMtM3tmaWxsOiNiY2JlYzA7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5GaXJlIEdyYXk8L3RpdGxlPjxnIGlkPSJMYXllcl8yIiBkYXRhLW5hbWU9IkxheWVyIDIiPjxnIGlkPSJMYXllcl8yLTIiIGRhdGEtbmFtZT0iTGF5ZXIgMiI+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNMzAuNzcsMGgwQTEyNi4yNSwxMjYuMjUsMCwwLDEsNDguMzIsMjAuNTNjNi45MSwxMC4xMywxMy4yMiwyMi45NSwxMy4yMiwzNiwwLDE5LjI3LTEzLjc3LDMwLjc3LTMwLjc3LDMwLjc3aDBBMzAuOCwzMC44LDAsMCwxLDAsNTYuNTNDMCwyOC42OCwzMC43NywwLDMwLjc3LDBaIi8+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNMzAuNzcsMjBoMHMyMy43LDIxLjM0LDIzLjcsNDMuNTRjMCwxNC44NC0xMC42MSwyMy43LTIzLjcsMjMuN2gwYTIzLjczLDIzLjczLDAsMCwxLTIzLjctMjMuN0M3LjA3LDQyLjE0LDMwLjc3LDIwLDMwLjc3LDIwWiIvPjxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTMwLjc3LDUyLjA3aDBTNDMuMTgsNjMuMjUsNDMuMTgsNzQuODhjMCw3Ljc3LTUuNTYsMTIuNDItMTIuNDIsMTIuNDJoMEExMi40MywxMi40MywwLDAsMSwxOC4zNSw3NC44OEMxOC4zNSw2My42NCwzMC43Nyw1Mi4wNywzMC43Nyw1Mi4wN1oiLz48L2c+PC9nPjwvc3ZnPg==" - }, - "asset-1343672d-7c02-4402-929e-0f8fef69cddd": { - "id": "asset-1343672d-7c02-4402-929e-0f8fef69cddd", - "@created": "2018-10-13T14:50:42.520Z", - "type": "dataurl", - "value": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNC44NiAyNC44NiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNkOTI4NTk7fS5jbHMtMntmaWxsOm5vbmU7c3Ryb2tlOiNmZmY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLXdpZHRoOjNweDt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPlBpbmsgWDwvdGl0bGU+PGcgaWQ9IkxheWVyXzIiIGRhdGEtbmFtZT0iTGF5ZXIgMiI+PGcgaWQ9IkxheWVyXzItMiIgZGF0YS1uYW1lPSJMYXllciAyIj48Y2lyY2xlIGNsYXNzPSJjbHMtMSIgY3g9IjEyLjQzIiBjeT0iMTIuNDMiIHI9IjEyLjQzIi8+PGxpbmUgY2xhc3M9ImNscy0yIiB4MT0iMTYuNzYiIHkxPSI4LjA5IiB4Mj0iOC4wOSIgeTI9IjE2Ljc2Ii8+PGxpbmUgY2xhc3M9ImNscy0yIiB4MT0iOC4wOSIgeTE9IjguMDkiIHgyPSIxNi43NiIgeTI9IjE2Ljc2Ii8+PC9nPjwvZz48L3N2Zz4=" - }, - "asset-0a807073-d056-4c7b-9bf4-225b71e47243": { - "id": "asset-0a807073-d056-4c7b-9bf4-225b71e47243", - "@created": "2018-10-13T14:50:49.318Z", - "type": "dataurl", - "value": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNC44NiAyNC44NiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiM0OGMxMDA7fS5jbHMtMntmaWxsOm5vbmU7c3Ryb2tlOiNmZmY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLXdpZHRoOjNweDt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPkdyZWVuIGNoZWNrPC90aXRsZT48ZyBpZD0iTGF5ZXJfMiIgZGF0YS1uYW1lPSJMYXllciAyIj48ZyBpZD0iTGF5ZXJfMi0yIiBkYXRhLW5hbWU9IkxheWVyIDIiPjxjaXJjbGUgY2xhc3M9ImNscy0xIiBjeD0iMTIuNDMiIGN5PSIxMi40MyIgcj0iMTIuNDMiLz48bGluZSBjbGFzcz0iY2xzLTIiIHgxPSIxOC43NCIgeTE9IjguNTMiIHgyPSIxMC4wNyIgeTI9IjE3LjIiLz48bGluZSBjbGFzcz0iY2xzLTIiIHgxPSI2LjgzIiB5MT0iMTIuMzUiIHgyPSIxMS45MyIgeTI9IjE3LjQ1Ii8+PC9nPjwvZz48L3N2Zz4=" - }, - "asset-86622ebc-32db-40fa-8310-262c85a10979": { - "id": "asset-86622ebc-32db-40fa-8310-262c85a10979", - "@created": "2018-10-13T15:01:15.036Z", - "type": "dataurl", - "value": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzNy45NCAyOS43Ij48ZGVmcz48c3R5bGU+LmNscy0xe2ZpbGw6IzQxNDA0MjtzdHJva2U6I2ZmZjtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzdHJva2Utd2lkdGg6MnB4O308L3N0eWxlPjwvZGVmcz48dGl0bGU+R2VvIEljb248L3RpdGxlPjxnIGlkPSJMYXllcl8yIiBkYXRhLW5hbWU9IkxheWVyIDIiPjxnIGlkPSJMYXllcl8yLTIiIGRhdGEtbmFtZT0iTGF5ZXIgMiI+PHJlY3QgY2xhc3M9ImNscy0xIiB4PSIxIiB5PSI3LjM1IiB3aWR0aD0iMzUuOTQiIGhlaWdodD0iMjEuMzYiLz48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0yMy41OCwxOS43aDBTMTcsMTMuNzYsMTcsNy41OUE2LjMsNi4zLDAsMCwxLDIzLjU4LDFoMGE2LjYsNi42LDAsMCwxLDYuNTksNi41OUMzMC4xOCwxMy41NiwyMy41OCwxOS43LDIzLjU4LDE5LjdaIi8+PC9nPjwvZz48L3N2Zz4=" - }, - "asset-c2128a19-e5ba-450c-99c0-a68abbdfa684": { - "id": "asset-c2128a19-e5ba-450c-99c0-a68abbdfa684", - "@created": "2018-10-13T15:02:05.173Z", - "type": "dataurl", - "value": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMS44MyAyNi4zOSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiM0MTQwNDI7c3Ryb2tlOiNmZmY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLXdpZHRoOjJweDt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPlJlcXVlc3QgSWNvbjwvdGl0bGU+PGcgaWQ9IkxheWVyXzIiIGRhdGEtbmFtZT0iTGF5ZXIgMiI+PGcgaWQ9IkxheWVyXzItMiIgZGF0YS1uYW1lPSJMYXllciAyIj48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0zMC44MywxSDFWMjUuMjNhLjE2LjE2LDAsMCwwLC4yOC4xMkw3LjY2LDE5SDMwLjgzWiIvPjwvZz48L2c+PC9zdmc+" - }, - "asset-e9118351-dcc5-4a27-a7fc-f15eb308999b": { - "id": "asset-e9118351-dcc5-4a27-a7fc-f15eb308999b", - "@created": "2018-10-13T15:02:24.299Z", - "type": "dataurl", - "value": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MC44MSAyNS4zMSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiM0MTQwNDI7c3Ryb2tlOiNmZmY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLXdpZHRoOjJweDt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPk1hY2hpbmUgSWNvbjwvdGl0bGU+PGcgaWQ9IkxheWVyXzIiIGRhdGEtbmFtZT0iTGF5ZXIgMiI+PGcgaWQ9IkxheWVyXzItMiIgZGF0YS1uYW1lPSJMYXllciAyIj48cmVjdCBjbGFzcz0iY2xzLTEiIHg9IjUuMTgiIHk9IjEiIHdpZHRoPSIyOS44MyIgaGVpZ2h0PSIxNy45NiIvPjxsaW5lIGNsYXNzPSJjbHMtMSIgeTE9IjI0LjMxIiB4Mj0iNDAuODEiIHkyPSIyNC4zMSIvPjwvZz48L2c+PC9zdmc+" - } - }, - "css": ".canvasPage {\n\n}" - } - } -] \ No newline at end of file diff --git a/x-pack/plugins/cases/common/schema/index.test.ts b/x-pack/plugins/cases/common/schema/index.test.ts index ae1146b594dbb..64eb2ad393fcb 100644 --- a/x-pack/plugins/cases/common/schema/index.test.ts +++ b/x-pack/plugins/cases/common/schema/index.test.ts @@ -13,6 +13,7 @@ import { limitedStringSchema, NonEmptyString, paginationSchema, + limitedNumberAsIntegerSchema, } from '.'; import { MAX_DOCS_PER_PAGE } from '../constants'; @@ -319,4 +320,69 @@ describe('schema', () => { `); }); }); + + describe('limitedNumberAsIntegerSchema', () => { + it('works correctly the number is safe integer', () => { + expect(PathReporter.report(limitedNumberAsIntegerSchema({ fieldName: 'foo' }).decode(1))) + .toMatchInlineSnapshot(` + Array [ + "No errors!", + ] + `); + }); + + it('fails when given a number that is lower than the minimum', () => { + expect( + PathReporter.report( + limitedNumberAsIntegerSchema({ fieldName: 'foo' }).decode(Number.MIN_SAFE_INTEGER - 1) + ) + ).toMatchInlineSnapshot(` + Array [ + "The foo field should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.", + ] + `); + }); + + it('fails when given a number that is higher than the maximum', () => { + expect( + PathReporter.report( + limitedNumberAsIntegerSchema({ fieldName: 'foo' }).decode(Number.MAX_SAFE_INTEGER + 1) + ) + ).toMatchInlineSnapshot(` + Array [ + "The foo field should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.", + ] + `); + }); + + it('fails when given a null instead of a number', () => { + expect(PathReporter.report(limitedNumberAsIntegerSchema({ fieldName: 'foo' }).decode(null))) + .toMatchInlineSnapshot(` + Array [ + "Invalid value null supplied to : LimitedNumberAsInteger", + ] + `); + }); + + it('fails when given a string instead of a number', () => { + expect( + PathReporter.report( + limitedNumberAsIntegerSchema({ fieldName: 'foo' }).decode('some string') + ) + ).toMatchInlineSnapshot(` + Array [ + "Invalid value \\"some string\\" supplied to : LimitedNumberAsInteger", + ] + `); + }); + + it('fails when given a float number instead of an safe integer number', () => { + expect(PathReporter.report(limitedNumberAsIntegerSchema({ fieldName: 'foo' }).decode(1.2))) + .toMatchInlineSnapshot(` + Array [ + "The foo field should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.", + ] + `); + }); + }); }); diff --git a/x-pack/plugins/cases/common/schema/index.ts b/x-pack/plugins/cases/common/schema/index.ts index b38d499c8c04c..0bcbdcfb2c480 100644 --- a/x-pack/plugins/cases/common/schema/index.ts +++ b/x-pack/plugins/cases/common/schema/index.ts @@ -154,6 +154,24 @@ export const limitedNumberSchema = ({ fieldName, min, max }: LimitedSchemaType) rt.identity ); +export const limitedNumberAsIntegerSchema = ({ fieldName }: { fieldName: string }) => + new rt.Type( + 'LimitedNumberAsInteger', + rt.number.is, + (input, context) => + either.chain(rt.number.validate(input, context), (s) => { + if (!Number.isSafeInteger(s)) { + return rt.failure( + input, + context, + `The ${fieldName} field should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.` + ); + } + return rt.success(s); + }), + rt.identity + ); + export interface RegexStringSchemaType { codec: rt.Type; pattern: string; diff --git a/x-pack/plugins/cases/common/types/api/case/v1.test.ts b/x-pack/plugins/cases/common/types/api/case/v1.test.ts index a509bdee36525..baf9626d3562e 100644 --- a/x-pack/plugins/cases/common/types/api/case/v1.test.ts +++ b/x-pack/plugins/cases/common/types/api/case/v1.test.ts @@ -114,10 +114,15 @@ const basicCase: Case = { value: true, }, { - key: 'second_custom_field_key', + key: 'third_custom_field_key', type: CustomFieldTypes.TEXT, value: 'www.example.com', }, + { + key: 'fourth_custom_field_key', + type: CustomFieldTypes.NUMBER, + value: 3, + }, ], }; @@ -149,6 +154,11 @@ describe('CasePostRequestRt', () => { type: CustomFieldTypes.TOGGLE, value: true, }, + { + key: 'third_custom_field_key', + type: CustomFieldTypes.NUMBER, + value: 3, + }, ], }; @@ -322,6 +332,44 @@ describe('CasePostRequestRt', () => { ); }); + it(`throws an error when a number customFields is more than ${Number.MAX_SAFE_INTEGER}`, () => { + expect( + PathReporter.report( + CasePostRequestRt.decode({ + ...defaultRequest, + customFields: [ + { + key: 'first_custom_field_key', + type: CustomFieldTypes.NUMBER, + value: Number.MAX_SAFE_INTEGER + 1, + }, + ], + }) + ) + ).toContain( + `The value field should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.` + ); + }); + + it(`throws an error when a number customFields is less than ${Number.MIN_SAFE_INTEGER}`, () => { + expect( + PathReporter.report( + CasePostRequestRt.decode({ + ...defaultRequest, + customFields: [ + { + key: 'first_custom_field_key', + type: CustomFieldTypes.NUMBER, + value: Number.MIN_SAFE_INTEGER - 1, + }, + ], + }) + ) + ).toContain( + `The value field should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.` + ); + }); + it('throws an error when a text customField is an empty string', () => { expect( PathReporter.report( @@ -665,6 +713,11 @@ describe('CasePatchRequestRt', () => { type: 'toggle', value: true, }, + { + key: 'third_custom_field_key', + type: 'number', + value: 123, + }, ], }; diff --git a/x-pack/plugins/cases/common/types/api/case/v1.ts b/x-pack/plugins/cases/common/types/api/case/v1.ts index 7a45f92fa4668..f66df68169e5b 100644 --- a/x-pack/plugins/cases/common/types/api/case/v1.ts +++ b/x-pack/plugins/cases/common/types/api/case/v1.ts @@ -29,7 +29,11 @@ import { NonEmptyString, paginationSchema, } from '../../../schema'; -import { CaseCustomFieldToggleRt, CustomFieldTextTypeRt } from '../../domain'; +import { + CaseCustomFieldToggleRt, + CustomFieldTextTypeRt, + CustomFieldNumberTypeRt, +} from '../../domain'; import { CaseRt, CaseSettingsRt, @@ -41,7 +45,10 @@ import { import { CaseConnectorRt } from '../../domain/connector/v1'; import { CaseUserProfileRt, UserRt } from '../../domain/user/v1'; import { CasesStatusResponseRt } from '../stats/v1'; -import { CaseCustomFieldTextWithValidationValueRt } from '../custom_field/v1'; +import { + CaseCustomFieldTextWithValidationValueRt, + CaseCustomFieldNumberWithValidationValueRt, +} from '../custom_field/v1'; const CaseCustomFieldTextWithValidationRt = rt.strict({ key: rt.string, @@ -49,7 +56,17 @@ const CaseCustomFieldTextWithValidationRt = rt.strict({ value: rt.union([CaseCustomFieldTextWithValidationValueRt('value'), rt.null]), }); -const CustomFieldRt = rt.union([CaseCustomFieldTextWithValidationRt, CaseCustomFieldToggleRt]); +const CaseCustomFieldNumberWithValidationRt = rt.strict({ + key: rt.string, + type: CustomFieldNumberTypeRt, + value: rt.union([CaseCustomFieldNumberWithValidationValueRt({ fieldName: 'value' }), rt.null]), +}); + +const CustomFieldRt = rt.union([ + CaseCustomFieldTextWithValidationRt, + CaseCustomFieldToggleRt, + CaseCustomFieldNumberWithValidationRt, +]); export const CaseRequestCustomFieldsRt = limitedArraySchema({ codec: CustomFieldRt, diff --git a/x-pack/plugins/cases/common/types/api/configure/v1.test.ts b/x-pack/plugins/cases/common/types/api/configure/v1.test.ts index c16dfbc60eaf7..64baf7b2e46f4 100644 --- a/x-pack/plugins/cases/common/types/api/configure/v1.test.ts +++ b/x-pack/plugins/cases/common/types/api/configure/v1.test.ts @@ -36,6 +36,7 @@ import { CustomFieldConfigurationWithoutTypeRt, TextCustomFieldConfigurationRt, ToggleCustomFieldConfigurationRt, + NumberCustomFieldConfigurationRt, TemplateConfigurationRt, } from './v1'; @@ -79,6 +80,12 @@ describe('configure', () => { type: CustomFieldTypes.TOGGLE, required: false, }, + { + key: 'number_custom_field', + label: 'Number custom field', + type: CustomFieldTypes.NUMBER, + required: false, + }, ], }; const query = ConfigurationRequestRt.decode(request); @@ -512,6 +519,93 @@ describe('configure', () => { }); }); + describe('NumberCustomFieldConfigurationRt', () => { + const defaultRequest = { + key: 'my_number_custom_field', + label: 'Number Custom Field', + type: CustomFieldTypes.NUMBER, + required: true, + }; + + it('has expected attributes in request', () => { + const query = NumberCustomFieldConfigurationRt.decode(defaultRequest); + + expect(query).toStrictEqual({ + _tag: 'Right', + right: { ...defaultRequest }, + }); + }); + + it('has expected attributes in request with defaultValue', () => { + const query = NumberCustomFieldConfigurationRt.decode({ + ...defaultRequest, + defaultValue: 1, + }); + + expect(query).toStrictEqual({ + _tag: 'Right', + right: { ...defaultRequest, defaultValue: 1 }, + }); + }); + + it('removes foo:bar attributes from request', () => { + const query = NumberCustomFieldConfigurationRt.decode({ ...defaultRequest, foo: 'bar' }); + + expect(query).toStrictEqual({ + _tag: 'Right', + right: { ...defaultRequest }, + }); + }); + + it('defaultValue fails if the type is string', () => { + expect( + PathReporter.report( + NumberCustomFieldConfigurationRt.decode({ + ...defaultRequest, + defaultValue: 'string', + }) + )[0] + ).toContain('Invalid value "string" supplied'); + }); + + it('defaultValue fails if the type is boolean', () => { + expect( + PathReporter.report( + NumberCustomFieldConfigurationRt.decode({ + ...defaultRequest, + defaultValue: false, + }) + )[0] + ).toContain('Invalid value false supplied'); + }); + + it(`throws an error if the default value is more than ${Number.MAX_SAFE_INTEGER}`, () => { + expect( + PathReporter.report( + NumberCustomFieldConfigurationRt.decode({ + ...defaultRequest, + defaultValue: Number.MAX_SAFE_INTEGER + 1, + }) + )[0] + ).toContain( + 'The defaultValue field should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.' + ); + }); + + it(`throws an error if the default value is less than ${Number.MIN_SAFE_INTEGER}`, () => { + expect( + PathReporter.report( + NumberCustomFieldConfigurationRt.decode({ + ...defaultRequest, + defaultValue: Number.MIN_SAFE_INTEGER - 1, + }) + )[0] + ).toContain( + 'The defaultValue field should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.' + ); + }); + }); + describe('TemplateConfigurationRt', () => { const defaultRequest = { key: 'template_key_1', diff --git a/x-pack/plugins/cases/common/types/api/configure/v1.ts b/x-pack/plugins/cases/common/types/api/configure/v1.ts index bd2e1f5c11af0..52843da1ac1ad 100644 --- a/x-pack/plugins/cases/common/types/api/configure/v1.ts +++ b/x-pack/plugins/cases/common/types/api/configure/v1.ts @@ -18,12 +18,19 @@ import { MAX_TEMPLATE_TAG_LENGTH, } from '../../../constants'; import { limitedArraySchema, limitedStringSchema, regexStringRt } from '../../../schema'; -import { CustomFieldTextTypeRt, CustomFieldToggleTypeRt } from '../../domain'; +import { + CustomFieldTextTypeRt, + CustomFieldToggleTypeRt, + CustomFieldNumberTypeRt, +} from '../../domain'; import type { Configurations, Configuration } from '../../domain/configure/v1'; import { ConfigurationBasicWithoutOwnerRt, ClosureTypeRt } from '../../domain/configure/v1'; import { CaseConnectorRt } from '../../domain/connector/v1'; import { CaseBaseOptionalFieldsRequestRt } from '../case/v1'; -import { CaseCustomFieldTextWithValidationValueRt } from '../custom_field/v1'; +import { + CaseCustomFieldTextWithValidationValueRt, + CaseCustomFieldNumberWithValidationValueRt, +} from '../custom_field/v1'; export const CustomFieldConfigurationWithoutTypeRt = rt.strict({ /** @@ -64,8 +71,25 @@ export const ToggleCustomFieldConfigurationRt = rt.intersection([ ), ]); +export const NumberCustomFieldConfigurationRt = rt.intersection([ + rt.strict({ type: CustomFieldNumberTypeRt }), + CustomFieldConfigurationWithoutTypeRt, + rt.exact( + rt.partial({ + defaultValue: rt.union([ + CaseCustomFieldNumberWithValidationValueRt({ fieldName: 'defaultValue' }), + rt.null, + ]), + }) + ), +]); + export const CustomFieldsConfigurationRt = limitedArraySchema({ - codec: rt.union([TextCustomFieldConfigurationRt, ToggleCustomFieldConfigurationRt]), + codec: rt.union([ + TextCustomFieldConfigurationRt, + ToggleCustomFieldConfigurationRt, + NumberCustomFieldConfigurationRt, + ]), min: 0, max: MAX_CUSTOM_FIELDS_PER_CASE, fieldName: 'customFields', diff --git a/x-pack/plugins/cases/common/types/api/custom_field/v1.test.ts b/x-pack/plugins/cases/common/types/api/custom_field/v1.test.ts index 83d9a437c998d..d17c936ff4463 100644 --- a/x-pack/plugins/cases/common/types/api/custom_field/v1.test.ts +++ b/x-pack/plugins/cases/common/types/api/custom_field/v1.test.ts @@ -7,7 +7,11 @@ import { PathReporter } from 'io-ts/lib/PathReporter'; import { MAX_CUSTOM_FIELD_TEXT_VALUE_LENGTH } from '../../../constants'; -import { CaseCustomFieldTextWithValidationValueRt, CustomFieldPutRequestRt } from './v1'; +import { + CaseCustomFieldTextWithValidationValueRt, + CustomFieldPutRequestRt, + CaseCustomFieldNumberWithValidationValueRt, +} from './v1'; describe('Custom Fields', () => { describe('CaseCustomFieldTextWithValidationValueRt', () => { @@ -100,4 +104,34 @@ describe('Custom Fields', () => { ).toContain('The value field cannot be an empty string.'); }); }); + + describe('CaseCustomFieldNumberWithValidationValueRt', () => { + const numberCustomFieldValueType = CaseCustomFieldNumberWithValidationValueRt({ + fieldName: 'value', + }); + it('should decode number correctly', () => { + const query = numberCustomFieldValueType.decode(123); + + expect(query).toStrictEqual({ + _tag: 'Right', + right: 123, + }); + }); + + it('should not be more than Number.MAX_SAFE_INTEGER', () => { + expect( + PathReporter.report(numberCustomFieldValueType.decode(Number.MAX_SAFE_INTEGER + 1))[0] + ).toContain( + 'The value field should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.' + ); + }); + + it('should not be less than Number.MIN_SAFE_INTEGER', () => { + expect( + PathReporter.report(numberCustomFieldValueType.decode(Number.MIN_SAFE_INTEGER - 1))[0] + ).toContain( + 'The value field should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.' + ); + }); + }); }); diff --git a/x-pack/plugins/cases/common/types/api/custom_field/v1.ts b/x-pack/plugins/cases/common/types/api/custom_field/v1.ts index fb59f187991b3..c3e618278adbe 100644 --- a/x-pack/plugins/cases/common/types/api/custom_field/v1.ts +++ b/x-pack/plugins/cases/common/types/api/custom_field/v1.ts @@ -7,7 +7,7 @@ import * as rt from 'io-ts'; import { MAX_CUSTOM_FIELD_TEXT_VALUE_LENGTH } from '../../../constants'; -import { limitedStringSchema } from '../../../schema'; +import { limitedStringSchema, limitedNumberAsIntegerSchema } from '../../../schema'; export const CaseCustomFieldTextWithValidationValueRt = (fieldName: string) => limitedStringSchema({ @@ -16,12 +16,22 @@ export const CaseCustomFieldTextWithValidationValueRt = (fieldName: string) => max: MAX_CUSTOM_FIELD_TEXT_VALUE_LENGTH, }); +export const CaseCustomFieldNumberWithValidationValueRt = ({ fieldName }: { fieldName: string }) => + limitedNumberAsIntegerSchema({ + fieldName, + }); + /** * Update custom_field */ export const CustomFieldPutRequestRt = rt.strict({ - value: rt.union([rt.boolean, rt.null, CaseCustomFieldTextWithValidationValueRt('value')]), + value: rt.union([ + rt.boolean, + rt.null, + CaseCustomFieldTextWithValidationValueRt('value'), + CaseCustomFieldNumberWithValidationValueRt({ fieldName: 'value' }), + ]), caseVersion: rt.string, }); diff --git a/x-pack/plugins/cases/common/types/domain/case/v1.test.ts b/x-pack/plugins/cases/common/types/domain/case/v1.test.ts index 267e08d205f15..b0a6f96bcacd0 100644 --- a/x-pack/plugins/cases/common/types/domain/case/v1.test.ts +++ b/x-pack/plugins/cases/common/types/domain/case/v1.test.ts @@ -85,6 +85,11 @@ const basicCase = { type: 'toggle', value: true, }, + { + key: 'third_custom_field_key', + type: 'number', + value: 0, + }, ], }; @@ -193,6 +198,11 @@ describe('CaseAttributesRt', () => { type: 'toggle', value: true, }, + { + key: 'third_custom_field_key', + type: 'number', + value: 0, + }, ], }; diff --git a/x-pack/plugins/cases/common/types/domain/configure/v1.test.ts b/x-pack/plugins/cases/common/types/domain/configure/v1.test.ts index 13637fb4d8c68..59682de1e7c7a 100644 --- a/x-pack/plugins/cases/common/types/domain/configure/v1.test.ts +++ b/x-pack/plugins/cases/common/types/domain/configure/v1.test.ts @@ -16,6 +16,7 @@ import { TemplateConfigurationRt, TextCustomFieldConfigurationRt, ToggleCustomFieldConfigurationRt, + NumberCustomFieldConfigurationRt, } from './v1'; describe('configure', () => { @@ -47,6 +48,13 @@ describe('configure', () => { required: false, }; + const numberCustomField = { + key: 'number_custom_field', + label: 'Number custom field', + type: CustomFieldTypes.NUMBER, + required: false, + }; + const templateWithAllCaseFields = { key: 'template_sample_1', name: 'Sample template 1', @@ -98,7 +106,7 @@ describe('configure', () => { const defaultRequest = { connector: resilient, closure_type: 'close-by-user', - customFields: [textCustomField, toggleCustomField], + customFields: [textCustomField, toggleCustomField, numberCustomField], templates: [], owner: 'cases', created_at: '2020-02-19T23:06:33.798Z', @@ -122,7 +130,7 @@ describe('configure', () => { _tag: 'Right', right: { ...defaultRequest, - customFields: [textCustomField, toggleCustomField], + customFields: [textCustomField, toggleCustomField, numberCustomField], }, }); }); @@ -134,7 +142,7 @@ describe('configure', () => { _tag: 'Right', right: { ...defaultRequest, - customFields: [textCustomField, toggleCustomField], + customFields: [textCustomField, toggleCustomField, numberCustomField], }, }); }); @@ -142,14 +150,14 @@ describe('configure', () => { it('removes foo:bar attributes from custom fields', () => { const query = ConfigurationAttributesRt.decode({ ...defaultRequest, - customFields: [{ ...textCustomField, foo: 'bar' }, toggleCustomField], + customFields: [{ ...textCustomField, foo: 'bar' }, toggleCustomField, numberCustomField], }); expect(query).toStrictEqual({ _tag: 'Right', right: { ...defaultRequest, - customFields: [textCustomField, toggleCustomField], + customFields: [textCustomField, toggleCustomField, numberCustomField], }, }); }); @@ -351,6 +359,62 @@ describe('configure', () => { }); }); + describe('NumberCustomFieldConfigurationRt', () => { + const defaultRequest = { + key: 'my_number_custom_field', + label: 'Number Custom Field', + type: CustomFieldTypes.NUMBER, + required: false, + }; + + it('has expected attributes in request with required: false', () => { + const query = NumberCustomFieldConfigurationRt.decode(defaultRequest); + + expect(query).toStrictEqual({ + _tag: 'Right', + right: { ...defaultRequest }, + }); + }); + + it('has expected attributes in request with defaultValue and required: true', () => { + const query = NumberCustomFieldConfigurationRt.decode({ + ...defaultRequest, + required: true, + defaultValue: 0, + }); + + expect(query).toStrictEqual({ + _tag: 'Right', + right: { + ...defaultRequest, + required: true, + defaultValue: 0, + }, + }); + }); + + it('defaultValue fails if the type is not number', () => { + expect( + PathReporter.report( + NumberCustomFieldConfigurationRt.decode({ + ...defaultRequest, + required: true, + defaultValue: 'foobar', + }) + )[0] + ).toContain('Invalid value "foobar" supplied'); + }); + + it('removes foo:bar attributes from request', () => { + const query = NumberCustomFieldConfigurationRt.decode({ ...defaultRequest, foo: 'bar' }); + + expect(query).toStrictEqual({ + _tag: 'Right', + right: { ...defaultRequest }, + }); + }); + }); + describe('TemplateConfigurationRt', () => { const defaultRequest = templateWithAllCaseFields; diff --git a/x-pack/plugins/cases/common/types/domain/configure/v1.ts b/x-pack/plugins/cases/common/types/domain/configure/v1.ts index 1e4e30c95e381..17760922d2cda 100644 --- a/x-pack/plugins/cases/common/types/domain/configure/v1.ts +++ b/x-pack/plugins/cases/common/types/domain/configure/v1.ts @@ -8,7 +8,11 @@ import * as rt from 'io-ts'; import { CaseConnectorRt, ConnectorMappingsRt } from '../connector/v1'; import { UserRt } from '../user/v1'; -import { CustomFieldTextTypeRt, CustomFieldToggleTypeRt } from '../custom_field/v1'; +import { + CustomFieldTextTypeRt, + CustomFieldToggleTypeRt, + CustomFieldNumberTypeRt, +} from '../custom_field/v1'; import { CaseBaseOptionalFieldsRt } from '../case/v1'; export const ClosureTypeRt = rt.union([ @@ -51,9 +55,20 @@ export const ToggleCustomFieldConfigurationRt = rt.intersection([ ), ]); +export const NumberCustomFieldConfigurationRt = rt.intersection([ + rt.strict({ type: CustomFieldNumberTypeRt }), + CustomFieldConfigurationWithoutTypeRt, + rt.exact( + rt.partial({ + defaultValue: rt.union([rt.number, rt.null]), + }) + ), +]); + export const CustomFieldConfigurationRt = rt.union([ TextCustomFieldConfigurationRt, ToggleCustomFieldConfigurationRt, + NumberCustomFieldConfigurationRt, ]); export const CustomFieldsConfigurationRt = rt.array(CustomFieldConfigurationRt); diff --git a/x-pack/plugins/cases/common/types/domain/custom_field/v1.test.ts b/x-pack/plugins/cases/common/types/domain/custom_field/v1.test.ts index ea57d3e3201c1..5513325d30fb0 100644 --- a/x-pack/plugins/cases/common/types/domain/custom_field/v1.test.ts +++ b/x-pack/plugins/cases/common/types/domain/custom_field/v1.test.ts @@ -42,6 +42,22 @@ describe('CaseCustomFieldRt', () => { value: null, }, ], + [ + 'type number value number', + { + key: 'number_custom_field_1', + type: 'number', + value: 1, + }, + ], + [ + 'type number value null', + { + key: 'number_custom_field_2', + type: 'number', + value: null, + }, + ], ])(`has expected attributes for customField with %s`, (_, customField) => { const query = CaseCustomFieldRt.decode(customField); @@ -70,4 +86,14 @@ describe('CaseCustomFieldRt', () => { expect(PathReporter.report(query)[0]).toContain('Invalid value "hello" supplied'); }); + + it('fails if number type but value is a string', () => { + const query = CaseCustomFieldRt.decode({ + key: 'list_custom_field_1', + type: 'number', + value: 'hi', + }); + + expect(PathReporter.report(query)[0]).toContain('Invalid value "hi" supplied'); + }); }); diff --git a/x-pack/plugins/cases/common/types/domain/custom_field/v1.ts b/x-pack/plugins/cases/common/types/domain/custom_field/v1.ts index 4878fea326b04..d0f9404f8f113 100644 --- a/x-pack/plugins/cases/common/types/domain/custom_field/v1.ts +++ b/x-pack/plugins/cases/common/types/domain/custom_field/v1.ts @@ -9,10 +9,12 @@ import * as rt from 'io-ts'; export enum CustomFieldTypes { TEXT = 'text', TOGGLE = 'toggle', + NUMBER = 'number', } export const CustomFieldTextTypeRt = rt.literal(CustomFieldTypes.TEXT); export const CustomFieldToggleTypeRt = rt.literal(CustomFieldTypes.TOGGLE); +export const CustomFieldNumberTypeRt = rt.literal(CustomFieldTypes.NUMBER); const CaseCustomFieldTextRt = rt.strict({ key: rt.string, @@ -26,10 +28,21 @@ export const CaseCustomFieldToggleRt = rt.strict({ value: rt.union([rt.boolean, rt.null]), }); -export const CaseCustomFieldRt = rt.union([CaseCustomFieldTextRt, CaseCustomFieldToggleRt]); +export const CaseCustomFieldNumberRt = rt.strict({ + key: rt.string, + type: CustomFieldNumberTypeRt, + value: rt.union([rt.number, rt.null]), +}); + +export const CaseCustomFieldRt = rt.union([ + CaseCustomFieldTextRt, + CaseCustomFieldToggleRt, + CaseCustomFieldNumberRt, +]); export const CaseCustomFieldsRt = rt.array(CaseCustomFieldRt); export type CaseCustomFields = rt.TypeOf; export type CaseCustomField = rt.TypeOf; export type CaseCustomFieldToggle = rt.TypeOf; export type CaseCustomFieldText = rt.TypeOf; +export type CaseCustomFieldNumber = rt.TypeOf; diff --git a/x-pack/plugins/cases/public/common/translations.ts b/x-pack/plugins/cases/public/common/translations.ts index 2e11c3a64caae..7fa5b54db00ec 100644 --- a/x-pack/plugins/cases/public/common/translations.ts +++ b/x-pack/plugins/cases/public/common/translations.ts @@ -300,6 +300,12 @@ export const MAX_LENGTH_ERROR = (field: string, length: number) => 'The length of the {field} is too long. The maximum length is {length} characters.', }); +export const SAFE_INTEGER_NUMBER_ERROR = (field: string) => + i18n.translate('xpack.cases.customFields.safeIntegerNumberError', { + values: { field }, + defaultMessage: `The value of the {field} should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.`, + }); + export const MAX_TAGS_ERROR = (length: number) => i18n.translate('xpack.cases.createCase.maxTagsError', { values: { length }, diff --git a/x-pack/plugins/cases/public/components/all_cases/columns_popover.test.tsx b/x-pack/plugins/cases/public/components/all_cases/columns_popover.test.tsx index 7746c5f0a4f1b..84f2c9f3726d6 100644 --- a/x-pack/plugins/cases/public/components/all_cases/columns_popover.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/columns_popover.test.tsx @@ -14,7 +14,8 @@ import type { AppMockRenderer } from '../../common/mock'; import { createAppMockRenderer } from '../../common/mock'; import { ColumnsPopover } from './columns_popover'; -describe('ColumnsPopover', () => { +// FLAKY: https://github.com/elastic/kibana/issues/174682 +describe.skip('ColumnsPopover', () => { let appMockRenderer: AppMockRenderer; beforeEach(() => { diff --git a/x-pack/plugins/cases/public/components/case_form_fields/custom_fields.test.tsx b/x-pack/plugins/cases/public/components/case_form_fields/custom_fields.test.tsx index f11e5826ca91c..9a96b0a342771 100644 --- a/x-pack/plugins/cases/public/components/case_form_fields/custom_fields.test.tsx +++ b/x-pack/plugins/cases/public/components/case_form_fields/custom_fields.test.tsx @@ -78,7 +78,7 @@ describe.skip('CustomFields', () => { ); - expect(await screen.findAllByTestId('form-optional-field-label')).toHaveLength(2); + expect(await screen.findAllByTestId('form-optional-field-label')).toHaveLength(4); }); it('should not set default value when in edit mode', async () => { @@ -115,12 +115,14 @@ describe.skip('CustomFields', () => { const customFields = customFieldsWrapper.querySelectorAll('.euiFormRow'); - expect(customFields).toHaveLength(4); + expect(customFields).toHaveLength(6); expect(customFields[0]).toHaveTextContent('My test label 1'); expect(customFields[1]).toHaveTextContent('My test label 2'); expect(customFields[2]).toHaveTextContent('My test label 3'); expect(customFields[3]).toHaveTextContent('My test label 4'); + expect(customFields[4]).toHaveTextContent('My test label 5'); + expect(customFields[5]).toHaveTextContent('My test label 6'); }); it('should update the custom fields', async () => { @@ -132,6 +134,7 @@ describe.skip('CustomFields', () => { const textField = customFieldsConfigurationMock[2]; const toggleField = customFieldsConfigurationMock[3]; + const numberField = customFieldsConfigurationMock[5]; await userEvent.type( await screen.findByTestId(`${textField.key}-${textField.type}-create-custom-field`), @@ -140,6 +143,10 @@ describe.skip('CustomFields', () => { await userEvent.click( await screen.findByTestId(`${toggleField.key}-${toggleField.type}-create-custom-field`) ); + await userEvent.type( + await screen.findByTestId(`${numberField.key}-${numberField.type}-create-custom-field`), + '4' + ); await userEvent.click(await screen.findByText('Submit')); @@ -152,6 +159,8 @@ describe.skip('CustomFields', () => { [customFieldsConfigurationMock[1].key]: customFieldsConfigurationMock[1].defaultValue, [textField.key]: 'hello', [toggleField.key]: true, + [customFieldsConfigurationMock[4].key]: customFieldsConfigurationMock[4].defaultValue, + [numberField.key]: '4', }, }, true diff --git a/x-pack/plugins/cases/public/components/case_form_fields/index.test.tsx b/x-pack/plugins/cases/public/components/case_form_fields/index.test.tsx index ac162e41a47e4..438b0a24841e9 100644 --- a/x-pack/plugins/cases/public/components/case_form_fields/index.test.tsx +++ b/x-pack/plugins/cases/public/components/case_form_fields/index.test.tsx @@ -206,6 +206,7 @@ describe('CaseFormFields', () => { const textField = customFieldsConfigurationMock[0]; const toggleField = customFieldsConfigurationMock[1]; + const numberField = customFieldsConfigurationMock[4]; const textCustomField = await screen.findByTestId( `${textField.key}-${textField.type}-create-custom-field` @@ -219,6 +220,13 @@ describe('CaseFormFields', () => { await screen.findByTestId(`${toggleField.key}-${toggleField.type}-create-custom-field`) ); + const numberCustomField = await screen.findByTestId( + `${numberField.key}-${numberField.type}-create-custom-field` + ); + + await user.clear(numberCustomField); + await user.paste('4321'); + await user.click(await screen.findByText('Submit')); await waitFor(() => { @@ -230,6 +238,7 @@ describe('CaseFormFields', () => { test_key_1: 'My text test value 1', test_key_2: false, test_key_4: false, + test_key_5: '4321', }, }, true @@ -268,6 +277,7 @@ describe('CaseFormFields', () => { test_key_1: 'Test custom filed value', test_key_2: true, test_key_4: false, + test_key_5: 123, }, }, true diff --git a/x-pack/plugins/cases/public/components/case_view/components/custom_fields.test.tsx b/x-pack/plugins/cases/public/components/case_view/components/custom_fields.test.tsx index 3b9d762137abb..67d8f8fd05764 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/custom_fields.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/components/custom_fields.test.tsx @@ -89,7 +89,7 @@ describe('Case View Page files tab', () => { exact: false, }); - expect(customFields.length).toBe(4); + expect(customFields.length).toBe(6); expect(await within(customFields[0]).findByRole('heading')).toHaveTextContent( 'My test label 1' @@ -103,6 +103,12 @@ describe('Case View Page files tab', () => { expect(await within(customFields[3]).findByRole('heading')).toHaveTextContent( 'My test label 4' ); + expect(await within(customFields[4]).findByRole('heading')).toHaveTextContent( + 'My test label 5' + ); + expect(await within(customFields[5]).findByRole('heading')).toHaveTextContent( + 'My test label 6' + ); }); it('pass the permissions to custom fields correctly', async () => { diff --git a/x-pack/plugins/cases/public/components/case_view/components/user_list.test.tsx b/x-pack/plugins/cases/public/components/case_view/components/user_list.test.tsx index 7254f7f500f63..b1601e471915f 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/user_list.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/components/user_list.test.tsx @@ -20,7 +20,8 @@ jest.mock('../../../common/navigation/hooks'); const useCaseViewNavigationMock = useCaseViewNavigation as jest.Mock; -describe('UserList ', () => { +// FLAKY: https://github.com/elastic/kibana/issues/192640 +describe.skip('UserList ', () => { const title = basicCase.title; const caseLink = 'https://example.com/cases/test'; const user = { diff --git a/x-pack/plugins/cases/public/components/configure_cases/flyout.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/flyout.test.tsx index b3c782f83fb50..8b42dd7df6f0d 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/flyout.test.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/flyout.test.tsx @@ -612,6 +612,16 @@ describe('CommonFlyout ', () => { type: 'toggle', value: false, }, + { + key: 'test_key_5', + type: 'number', + value: 123, + }, + { + key: 'test_key_6', + type: 'number', + value: null, + }, ], }, }); diff --git a/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx index e058d982e7367..7a29c959d2525 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx @@ -715,6 +715,8 @@ describe('ConfigureCases', () => { { ...customFieldsConfigurationMock[1] }, { ...customFieldsConfigurationMock[2] }, { ...customFieldsConfigurationMock[3] }, + { ...customFieldsConfigurationMock[4] }, + { ...customFieldsConfigurationMock[5] }, ], templates: [], id: '', @@ -774,6 +776,8 @@ describe('ConfigureCases', () => { { ...customFieldsConfigurationMock[1] }, { ...customFieldsConfigurationMock[2] }, { ...customFieldsConfigurationMock[3] }, + { ...customFieldsConfigurationMock[4] }, + { ...customFieldsConfigurationMock[5] }, ], templates: [ { @@ -867,6 +871,16 @@ describe('ConfigureCases', () => { type: customFieldsConfigurationMock[3].type, value: false, }, + { + key: customFieldsConfigurationMock[4].key, + type: customFieldsConfigurationMock[4].type, + value: customFieldsConfigurationMock[4].defaultValue, + }, + { + key: customFieldsConfigurationMock[5].key, + type: customFieldsConfigurationMock[5].type, + value: null, + }, { key: expect.anything(), type: CustomFieldTypes.TEXT as const, @@ -930,6 +944,8 @@ describe('ConfigureCases', () => { { ...customFieldsConfigurationMock[1] }, { ...customFieldsConfigurationMock[2] }, { ...customFieldsConfigurationMock[3] }, + { ...customFieldsConfigurationMock[4] }, + { ...customFieldsConfigurationMock[5] }, ], templates: [], id: '', @@ -1107,6 +1123,16 @@ describe('ConfigureCases', () => { type: customFieldsConfigurationMock[3].type, value: false, // when no default value for toggle, we set it to false }, + { + key: customFieldsConfigurationMock[4].key, + type: customFieldsConfigurationMock[4].type, + value: customFieldsConfigurationMock[4].defaultValue, + }, + { + key: customFieldsConfigurationMock[5].key, + type: customFieldsConfigurationMock[5].type, + value: null, + }, ], }, }, diff --git a/x-pack/plugins/cases/public/components/create/form_context.test.tsx b/x-pack/plugins/cases/public/components/create/form_context.test.tsx index 0f28e6f9db1c2..252726ef559c9 100644 --- a/x-pack/plugins/cases/public/components/create/form_context.test.tsx +++ b/x-pack/plugins/cases/public/components/create/form_context.test.tsx @@ -517,6 +517,7 @@ describe('Create case', () => { const textField = customFieldsConfigurationMock[0]; const toggleField = customFieldsConfigurationMock[1]; + const numberField = customFieldsConfigurationMock[4]; expect(await screen.findByTestId('caseCustomFields')).toBeInTheDocument(); @@ -532,6 +533,14 @@ describe('Create case', () => { await screen.findByTestId(`${toggleField.key}-${toggleField.type}-create-custom-field`) ); + const numberCustomField = await screen.findByTestId( + `${numberField.key}-${numberField.type}-create-custom-field` + ); + + await user.clear(numberCustomField); + await user.click(numberCustomField); + await user.paste('678'); + await user.click(await screen.findByTestId('create-case-submit')); await waitFor(() => expect(postCase).toHaveBeenCalled()); @@ -544,6 +553,8 @@ describe('Create case', () => { { ...customFieldsMock[1], value: false }, // toggled the default customFieldsMock[2], { ...customFieldsMock[3], value: false }, + { ...customFieldsMock[4], value: 678 }, + customFieldsMock[5], { key: 'my_custom_field_key', type: CustomFieldTypes.TEXT, diff --git a/x-pack/plugins/cases/public/components/custom_fields/builder.tsx b/x-pack/plugins/cases/public/components/custom_fields/builder.tsx index d2ee25d08bfa6..4baf050fd0f52 100644 --- a/x-pack/plugins/cases/public/components/custom_fields/builder.tsx +++ b/x-pack/plugins/cases/public/components/custom_fields/builder.tsx @@ -9,8 +9,10 @@ import type { CustomFieldBuilderMap } from './types'; import { CustomFieldTypes } from '../../../common/types/domain'; import { configureTextCustomFieldFactory } from './text/configure_text_field'; import { configureToggleCustomFieldFactory } from './toggle/configure_toggle_field'; +import { configureNumberCustomFieldFactory } from './number/configure_number_field'; export const builderMap = Object.freeze({ [CustomFieldTypes.TEXT]: configureTextCustomFieldFactory, [CustomFieldTypes.TOGGLE]: configureToggleCustomFieldFactory, + [CustomFieldTypes.NUMBER]: configureNumberCustomFieldFactory, } as const) as CustomFieldBuilderMap; 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 eaaa0e28747ea..0f87c04bc9ad3 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 @@ -59,13 +59,20 @@ describe('CustomFieldsList', () => { ) ).toBeInTheDocument(); expect((await screen.findAllByText('Text')).length).toBe(2); - expect((await screen.findAllByText('Required')).length).toBe(2); + expect((await screen.findAllByText('Required')).length).toBe(3); expect( await screen.findByTestId( `custom-field-${customFieldsConfigurationMock[1].key}-${customFieldsConfigurationMock[1].type}` ) ).toBeInTheDocument(); expect((await screen.findAllByText('Toggle')).length).toBe(2); + + expect( + await screen.findByTestId( + `custom-field-${customFieldsConfigurationMock[4].key}-${customFieldsConfigurationMock[4].type}` + ) + ).toBeInTheDocument(); + expect((await screen.findAllByText('Number')).length).toBe(2); }); it('shows single CustomFieldsList correctly', async () => { diff --git a/x-pack/plugins/cases/public/components/custom_fields/number/config.ts b/x-pack/plugins/cases/public/components/custom_fields/number/config.ts new file mode 100644 index 0000000000000..b73bc033883a8 --- /dev/null +++ b/x-pack/plugins/cases/public/components/custom_fields/number/config.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 type { FieldConfig } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; +import { REQUIRED_FIELD, SAFE_INTEGER_NUMBER_ERROR } from '../translations'; + +const { emptyField } = fieldValidators; + +export const getNumberFieldConfig = ({ + required, + label, + defaultValue, +}: { + required: boolean; + label: string; + defaultValue?: number; +}): FieldConfig => { + const validators = []; + + if (required) { + validators.push({ + validator: emptyField(REQUIRED_FIELD(label)), + }); + } + + return { + ...(defaultValue && { defaultValue }), + validations: [ + ...validators, + { + validator: ({ value }) => { + if (value == null) { + return; + } + const numericValue = Number(value); + + if (!Number.isSafeInteger(numericValue)) { + return { message: SAFE_INTEGER_NUMBER_ERROR(label) }; + } + }, + }, + ], + }; +}; diff --git a/x-pack/plugins/cases/public/components/custom_fields/number/configure.test.tsx b/x-pack/plugins/cases/public/components/custom_fields/number/configure.test.tsx new file mode 100644 index 0000000000000..f96e47ce30918 --- /dev/null +++ b/x-pack/plugins/cases/public/components/custom_fields/number/configure.test.tsx @@ -0,0 +1,108 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { FormTestComponent } from '../../../common/test_utils'; +import * as i18n from '../translations'; +import { Configure } from './configure'; + +describe('Configure ', () => { + const onSubmit = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders correctly', async () => { + render( + + + + ); + + expect(screen.getByText(i18n.FIELD_OPTION_REQUIRED)).toBeInTheDocument(); + }); + + it('updates field options without default value correctly when not required', async () => { + render( + + + + ); + + await userEvent.click(await screen.findByTestId('form-test-component-submit-button')); + + await waitFor(() => { + // data, isValid + expect(onSubmit).toBeCalledWith({}, true); + }); + }); + + it('updates field options with default value correctly when not required', async () => { + render( + + + + ); + + await userEvent.click(await screen.findByTestId('number-custom-field-default-value')); + await userEvent.paste('123'); + await userEvent.click(await screen.findByTestId('form-test-component-submit-button')); + + await waitFor(() => { + // data, isValid + expect(onSubmit).toBeCalledWith({ defaultValue: '123' }, true); + }); + }); + + it('updates field options with default value correctly when required', async () => { + render( + + + + ); + + await userEvent.click(await screen.findByTestId('number-custom-field-required')); + await userEvent.click(await screen.findByTestId('number-custom-field-default-value')); + await userEvent.paste('123'); + await userEvent.click(await screen.findByTestId('form-test-component-submit-button')); + + await waitFor(() => { + // data, isValid + expect(onSubmit).toBeCalledWith( + { + required: true, + defaultValue: '123', + }, + true + ); + }); + }); + + it('updates field options without default value correctly when required', async () => { + render( + + + + ); + + await userEvent.click(await screen.findByTestId('number-custom-field-required')); + await userEvent.click(await screen.findByTestId('form-test-component-submit-button')); + + await waitFor(() => { + // data, isValid + expect(onSubmit).toBeCalledWith( + { + required: true, + }, + true + ); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/components/custom_fields/number/configure.tsx b/x-pack/plugins/cases/public/components/custom_fields/number/configure.tsx new file mode 100644 index 0000000000000..db1fcffd0be0b --- /dev/null +++ b/x-pack/plugins/cases/public/components/custom_fields/number/configure.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 { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { CheckBoxField, NumericField } from '@kbn/es-ui-shared-plugin/static/forms/components'; +import type { CaseCustomFieldNumber } from '../../../../common/types/domain'; +import type { CustomFieldType } from '../types'; +import { getNumberFieldConfig } from './config'; +import * as i18n from '../translations'; + +const ConfigureComponent: CustomFieldType['Configure'] = () => { + const config = getNumberFieldConfig({ + required: false, + label: i18n.DEFAULT_VALUE.toLocaleLowerCase(), + }); + + return ( + <> + + + + ); +}; + +ConfigureComponent.displayName = 'Configure'; + +export const Configure = React.memo(ConfigureComponent); diff --git a/x-pack/plugins/cases/public/components/custom_fields/number/configure_number_field.test.ts b/x-pack/plugins/cases/public/components/custom_fields/number/configure_number_field.test.ts new file mode 100644 index 0000000000000..aee9a4439792d --- /dev/null +++ b/x-pack/plugins/cases/public/components/custom_fields/number/configure_number_field.test.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 { configureNumberCustomFieldFactory } from './configure_number_field'; + +describe('configureTextCustomFieldFactory ', () => { + const builder = configureNumberCustomFieldFactory(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders correctly', async () => { + expect(builder).toEqual({ + id: 'number', + label: 'Number', + getEuiTableColumn: expect.any(Function), + build: expect.any(Function), + }); + }); +}); diff --git a/x-pack/plugins/cases/public/components/custom_fields/number/configure_number_field.ts b/x-pack/plugins/cases/public/components/custom_fields/number/configure_number_field.ts new file mode 100644 index 0000000000000..428559f5f83c0 --- /dev/null +++ b/x-pack/plugins/cases/public/components/custom_fields/number/configure_number_field.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 { CustomFieldFactory } from '../types'; +import type { CaseCustomFieldNumber } from '../../../../common/types/domain'; + +import { CustomFieldTypes } from '../../../../common/types/domain'; +import * as i18n from '../translations'; +import { getEuiTableColumn } from './get_eui_table_column'; +import { Edit } from './edit'; +import { View } from './view'; +import { Configure } from './configure'; +import { Create } from './create'; + +export const configureNumberCustomFieldFactory: CustomFieldFactory = () => ({ + id: CustomFieldTypes.NUMBER, + label: i18n.NUMBER_LABEL, + getEuiTableColumn, + build: () => ({ + Configure, + Edit, + View, + Create, + }), +}); diff --git a/x-pack/plugins/cases/public/components/custom_fields/number/create.test.tsx b/x-pack/plugins/cases/public/components/custom_fields/number/create.test.tsx new file mode 100644 index 0000000000000..2a8a515df01ee --- /dev/null +++ b/x-pack/plugins/cases/public/components/custom_fields/number/create.test.tsx @@ -0,0 +1,225 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { FormTestComponent } from '../../../common/test_utils'; +import { Create } from './create'; +import { customFieldsConfigurationMock } from '../../../containers/mock'; + +describe('Create ', () => { + const onSubmit = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + // required number custom field with a default value + const customFieldConfiguration = customFieldsConfigurationMock[4]; + + it('renders correctly with default value and required', async () => { + render( + + + + ); + + expect(await screen.findByText(customFieldConfiguration.label)).toBeInTheDocument(); + + expect( + await screen.findByTestId(`${customFieldConfiguration.key}-number-create-custom-field`) + ).toHaveValue(customFieldConfiguration.defaultValue as number); + }); + + it('renders correctly without default value and not required', async () => { + const optionalField = customFieldsConfigurationMock[5]; // optional number custom field + + render( + + + + ); + + expect(await screen.findByText(optionalField.label)).toBeInTheDocument(); + expect( + await screen.findByTestId(`${optionalField.key}-number-create-custom-field`) + ).toHaveValue(null); + }); + + it('does not render default value when setDefaultValue is false', async () => { + render( + + + + ); + + expect( + await screen.findByTestId(`${customFieldConfiguration.key}-number-create-custom-field`) + ).toHaveValue(null); + }); + + it('renders loading state correctly', async () => { + render( + + + + ); + + expect(await screen.findByRole('progressbar')).toBeInTheDocument(); + }); + + it('disables the text when loading', async () => { + render( + + + + ); + + expect( + await screen.findByTestId(`${customFieldConfiguration.key}-number-create-custom-field`) + ).toHaveAttribute('disabled'); + }); + + it('updates the value correctly', async () => { + render( + + + + ); + + const numberCustomField = await screen.findByTestId( + `${customFieldConfiguration.key}-number-create-custom-field` + ); + + await userEvent.clear(numberCustomField); + await userEvent.click(numberCustomField); + await userEvent.paste('1234'); + await userEvent.click(await screen.findByText('Submit')); + + await waitFor(() => { + // data, isValid + expect(onSubmit).toHaveBeenCalledWith( + { + customFields: { + [customFieldConfiguration.key]: '1234', + }, + }, + true + ); + }); + }); + + it('shows error when number is too big', async () => { + render( + + + + ); + + const numberCustomField = await screen.findByTestId( + `${customFieldConfiguration.key}-number-create-custom-field` + ); + + await userEvent.clear(numberCustomField); + await userEvent.click(numberCustomField); + await userEvent.paste(`${Number.MAX_SAFE_INTEGER + 1}`); + + await userEvent.click(await screen.findByText('Submit')); + + expect( + await screen.findByText( + 'The value of the My test label 5 should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.' + ) + ).toBeInTheDocument(); + + await waitFor(() => { + expect(onSubmit).toHaveBeenCalledWith({}, false); + }); + }); + + it('shows error when number is too small', async () => { + render( + + + + ); + + const numberCustomField = await screen.findByTestId( + `${customFieldConfiguration.key}-number-create-custom-field` + ); + + await userEvent.clear(numberCustomField); + await userEvent.click(numberCustomField); + await userEvent.paste(`${Number.MIN_SAFE_INTEGER - 1}`); + + await userEvent.click(await screen.findByText('Submit')); + + expect( + await screen.findByText( + 'The value of the My test label 5 should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.' + ) + ).toBeInTheDocument(); + + await waitFor(() => { + expect(onSubmit).toHaveBeenCalledWith({}, false); + }); + }); + + it('shows error when number is required but is empty', async () => { + render( + + + + ); + + await userEvent.clear( + await screen.findByTestId(`${customFieldConfiguration.key}-number-create-custom-field`) + ); + await userEvent.click(await screen.findByText('Submit')); + + expect( + await screen.findByText(`${customFieldConfiguration.label} is required.`) + ).toBeInTheDocument(); + + await waitFor(() => { + expect(onSubmit).toHaveBeenCalledWith({}, false); + }); + }); + + it('does not show error when number is not required but is empty', async () => { + render( + + + + ); + + await userEvent.click(await screen.findByText('Submit')); + + await waitFor(() => { + expect(onSubmit).toHaveBeenCalledWith({}, true); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/components/custom_fields/number/create.tsx b/x-pack/plugins/cases/public/components/custom_fields/number/create.tsx new file mode 100644 index 0000000000000..bc01145fd5d46 --- /dev/null +++ b/x-pack/plugins/cases/public/components/custom_fields/number/create.tsx @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { NumericField } from '@kbn/es-ui-shared-plugin/static/forms/components'; +import type { CaseCustomFieldNumber } from '../../../../common/types/domain'; +import type { CustomFieldType } from '../types'; +import { getNumberFieldConfig } from './config'; +import { OptionalFieldLabel } from '../../optional_field_label'; + +const CreateComponent: CustomFieldType['Create'] = ({ + customFieldConfiguration, + isLoading, + setAsOptional, + setDefaultValue = true, +}) => { + const { key, label, required, defaultValue } = customFieldConfiguration; + const config = getNumberFieldConfig({ + required: setAsOptional ? false : required, + label, + ...(defaultValue && + setDefaultValue && + !isNaN(Number(defaultValue)) && { defaultValue: Number(defaultValue) }), + }); + + return ( + + ); +}; + +CreateComponent.displayName = 'Create'; + +export const Create = React.memo(CreateComponent); diff --git a/x-pack/plugins/cases/public/components/custom_fields/number/edit.test.tsx b/x-pack/plugins/cases/public/components/custom_fields/number/edit.test.tsx new file mode 100644 index 0000000000000..fb19bdb553d41 --- /dev/null +++ b/x-pack/plugins/cases/public/components/custom_fields/number/edit.test.tsx @@ -0,0 +1,475 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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, waitFor } from '@testing-library/react'; +import { FormTestComponent } from '../../../common/test_utils'; +import { Edit } from './edit'; +import { customFieldsMock, customFieldsConfigurationMock } from '../../../containers/mock'; +import userEvent from '@testing-library/user-event'; +import type { CaseCustomFieldNumber } from '../../../../common/types/domain'; +import { POPULATED_WITH_DEFAULT } from '../translations'; + +describe('Edit ', () => { + const onSubmit = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + const customField = customFieldsMock[4] as CaseCustomFieldNumber; + const customFieldConfiguration = customFieldsConfigurationMock[4]; + + it('renders correctly', async () => { + render( + + + + ); + + expect(await screen.findByTestId('case-number-custom-field-test_key_5')).toBeInTheDocument(); + expect( + await screen.findByTestId('case-number-custom-field-edit-button-test_key_5') + ).toBeInTheDocument(); + expect(await screen.findByText(customFieldConfiguration.label)).toBeInTheDocument(); + expect(await screen.findByText('1234')).toBeInTheDocument(); + }); + + it('does not shows the edit button if the user does not have permissions', async () => { + render( + + + + ); + + expect( + screen.queryByTestId('case-number-custom-field-edit-button-test_key_1') + ).not.toBeInTheDocument(); + }); + + it('does not shows the edit button when loading', async () => { + render( + + + + ); + + expect( + screen.queryByTestId('case-number-custom-field-edit-button-test_key_1') + ).not.toBeInTheDocument(); + }); + + it('shows the loading spinner when loading', async () => { + render( + + + + ); + + expect( + await screen.findByTestId('case-number-custom-field-loading-test_key_5') + ).toBeInTheDocument(); + }); + + it('shows the no value number if the custom field is undefined', async () => { + render( + + + + ); + + expect(await screen.findByText('No value is added')).toBeInTheDocument(); + }); + + it('uses the required value correctly if a required field is empty', async () => { + render( + + + + ); + + expect(await screen.findByText('No value is added')).toBeInTheDocument(); + await userEvent.click( + await screen.findByTestId('case-number-custom-field-edit-button-test_key_5') + ); + + expect( + await screen.findByTestId( + `case-number-custom-field-form-field-${customFieldConfiguration.key}` + ) + ).toHaveValue(customFieldConfiguration.defaultValue as number); + expect( + await screen.findByText('This field is populated with the default value.') + ).toBeInTheDocument(); + + await userEvent.click( + await screen.findByTestId('case-number-custom-field-submit-button-test_key_5') + ); + + await waitFor(() => { + expect(onSubmit).toBeCalledWith({ + ...customField, + value: customFieldConfiguration.defaultValue, + }); + }); + }); + + it('does not show the value when the custom field is undefined', async () => { + render( + + + + ); + + expect(screen.queryByTestId('number-custom-field-view-test_key_5')).not.toBeInTheDocument(); + }); + + it('does not show the value when the value is null', async () => { + render( + + + + ); + + expect(screen.queryByTestId('number-custom-field-view-test_key_5')).not.toBeInTheDocument(); + }); + + it('does not show the form when the user does not have permissions', async () => { + render( + + + + ); + + expect( + screen.queryByTestId('case-number-custom-field-form-field-test_key_5') + ).not.toBeInTheDocument(); + expect( + screen.queryByTestId('case-number-custom-field-submit-button-test_key_5') + ).not.toBeInTheDocument(); + expect( + screen.queryByTestId('case-number-custom-field-cancel-button-test_key_5') + ).not.toBeInTheDocument(); + }); + + it('calls onSubmit when changing value', async () => { + render( + + + + ); + + await userEvent.click( + await screen.findByTestId('case-number-custom-field-edit-button-test_key_5') + ); + await userEvent.click( + await screen.findByTestId('case-number-custom-field-form-field-test_key_5') + ); + await userEvent.paste('12345'); + + expect( + await screen.findByTestId('case-number-custom-field-submit-button-test_key_5') + ).not.toBeDisabled(); + + await userEvent.click( + await screen.findByTestId('case-number-custom-field-submit-button-test_key_5') + ); + + await waitFor(() => { + expect(onSubmit).toBeCalledWith({ + ...customField, + value: 123412345, + }); + }); + }); + + it('calls onSubmit with defaultValue if no initialValue exists', async () => { + render( + + + + ); + + await userEvent.click( + await screen.findByTestId('case-number-custom-field-edit-button-test_key_5') + ); + + expect(await screen.findByText(POPULATED_WITH_DEFAULT)).toBeInTheDocument(); + expect(await screen.findByTestId('case-number-custom-field-form-field-test_key_5')).toHaveValue( + customFieldConfiguration.defaultValue as number + ); + expect( + await screen.findByTestId('case-number-custom-field-submit-button-test_key_5') + ).not.toBeDisabled(); + + await userEvent.click( + await screen.findByTestId('case-number-custom-field-submit-button-test_key_5') + ); + + await waitFor(() => { + expect(onSubmit).toBeCalledWith({ + ...customField, + value: customFieldConfiguration.defaultValue, + }); + }); + }); + + it('sets the value to null if the number field is empty', async () => { + render( + + + + ); + + await userEvent.click( + await screen.findByTestId('case-number-custom-field-edit-button-test_key_5') + ); + await userEvent.clear( + await screen.findByTestId('case-number-custom-field-form-field-test_key_5') + ); + + expect( + await screen.findByTestId('case-number-custom-field-submit-button-test_key_5') + ).not.toBeDisabled(); + + await userEvent.click( + await screen.findByTestId('case-number-custom-field-submit-button-test_key_5') + ); + + await waitFor(() => { + expect(onSubmit).toBeCalledWith({ + ...customField, + value: null, + }); + }); + }); + + it('hides the form when clicking the cancel button', async () => { + render( + + + + ); + + await userEvent.click( + await screen.findByTestId('case-number-custom-field-edit-button-test_key_5') + ); + + expect( + await screen.findByTestId('case-number-custom-field-form-field-test_key_5') + ).toBeInTheDocument(); + + await userEvent.click( + await screen.findByTestId('case-number-custom-field-cancel-button-test_key_5') + ); + + expect( + screen.queryByTestId('case-number-custom-field-form-field-test_key_5') + ).not.toBeInTheDocument(); + }); + + it('reset to initial value when canceling', async () => { + render( + + + + ); + + await userEvent.click( + await screen.findByTestId('case-number-custom-field-edit-button-test_key_5') + ); + await userEvent.click( + await screen.findByTestId('case-number-custom-field-form-field-test_key_5') + ); + await userEvent.paste('321'); + + expect( + await screen.findByTestId('case-number-custom-field-submit-button-test_key_5') + ).not.toBeDisabled(); + + await userEvent.click( + await screen.findByTestId('case-number-custom-field-cancel-button-test_key_5') + ); + + expect( + screen.queryByTestId('case-number-custom-field-form-field-test_key_5') + ).not.toBeInTheDocument(); + + await userEvent.click( + await screen.findByTestId('case-number-custom-field-edit-button-test_key_5') + ); + expect(await screen.findByTestId('case-number-custom-field-form-field-test_key_5')).toHaveValue( + 1234 + ); + }); + + it('shows validation error if the field is required', async () => { + render( + + + + ); + + await userEvent.click( + await screen.findByTestId('case-number-custom-field-edit-button-test_key_5') + ); + await userEvent.clear( + await screen.findByTestId('case-number-custom-field-form-field-test_key_5') + ); + + expect(await screen.findByText('My test label 5 is required.')).toBeInTheDocument(); + }); + + it('does not shows a validation error if the field is not required', async () => { + render( + + + + ); + + await userEvent.click( + await screen.findByTestId('case-number-custom-field-edit-button-test_key_5') + ); + await userEvent.clear( + await screen.findByTestId('case-number-custom-field-form-field-test_key_5') + ); + + expect( + await screen.findByTestId('case-number-custom-field-submit-button-test_key_5') + ).not.toBeDisabled(); + + expect(screen.queryByText('My test label 1 is required.')).not.toBeInTheDocument(); + }); + + it('shows validation error if the number is too big', async () => { + render( + + + + ); + + await userEvent.click( + await screen.findByTestId('case-number-custom-field-edit-button-test_key_5') + ); + await userEvent.clear( + await screen.findByTestId('case-number-custom-field-form-field-test_key_5') + ); + await userEvent.click( + await screen.findByTestId('case-number-custom-field-form-field-test_key_5') + ); + await userEvent.paste(`${2 ** 53 + 1}`); + + expect( + await screen.findByText( + 'The value of the My test label 5 should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.' + ) + ).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/cases/public/components/custom_fields/number/edit.tsx b/x-pack/plugins/cases/public/components/custom_fields/number/edit.tsx new file mode 100644 index 0000000000000..3ebb65a9dab8e --- /dev/null +++ b/x-pack/plugins/cases/public/components/custom_fields/number/edit.tsx @@ -0,0 +1,246 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect, useState, useCallback } from 'react'; +import { + EuiButton, + EuiButtonEmpty, + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiLoadingSpinner, + EuiText, +} from '@elastic/eui'; +import type { FormHook } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { + useForm, + UseField, + Form, + useFormData, +} from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { NumericField } from '@kbn/es-ui-shared-plugin/static/forms/components'; +import type { CaseCustomFieldNumber } from '../../../../common/types/domain'; +import { CustomFieldTypes } from '../../../../common/types/domain'; +import type { CasesConfigurationUICustomField } from '../../../../common/ui'; +import type { CustomFieldType } from '../types'; +import { View } from './view'; +import { + CANCEL, + EDIT_CUSTOM_FIELDS_ARIA_LABEL, + NO_CUSTOM_FIELD_SET, + SAVE, + POPULATED_WITH_DEFAULT, +} from '../translations'; +import { getNumberFieldConfig } from './config'; + +const isEmpty = (value: number | null | undefined) => { + return value == null; +}; + +interface FormState { + value: number | null; + isValid?: boolean; + submit: FormHook<{ value: number | null }>['submit']; +} + +interface FormWrapper { + initialValue: number | null; + isLoading: boolean; + customFieldConfiguration: CasesConfigurationUICustomField; + onChange: (state: FormState) => void; +} + +const FormWrapperComponent: React.FC = ({ + initialValue, + customFieldConfiguration, + isLoading, + onChange, +}) => { + const { form } = useForm<{ value: number | null }>({ + defaultValue: { + value: + customFieldConfiguration?.defaultValue != null && isEmpty(initialValue) + ? Number(customFieldConfiguration.defaultValue) + : initialValue, + }, + }); + + const [{ value }] = useFormData({ form }); + const { submit, isValid } = form; + const formFieldConfig = getNumberFieldConfig({ + required: customFieldConfiguration.required, + label: customFieldConfiguration.label, + }); + const populatedWithDefault = + value === customFieldConfiguration?.defaultValue && isEmpty(initialValue); + + useEffect(() => { + onChange({ + value, + isValid, + submit, + }); + }, [isValid, onChange, submit, value]); + + return ( +
+ + + ); +}; + +FormWrapperComponent.displayName = 'FormWrapper'; + +const EditComponent: CustomFieldType['Edit'] = ({ + customField, + customFieldConfiguration, + onSubmit, + isLoading, + canUpdate, +}) => { + const initialValue = customField?.value ?? null; + const [isEdit, setIsEdit] = useState(false); + const [formState, setFormState] = useState({ + isValid: undefined, + submit: async () => ({ isValid: false, data: { value: null } }), + value: initialValue, + }); + + const onEdit = useCallback(() => { + setIsEdit(true); + }, []); + + const onCancel = useCallback(() => { + setIsEdit(false); + }, []); + + const onSubmitCustomField = useCallback(async () => { + const { isValid, data } = await formState.submit(); + + if (isValid) { + onSubmit({ + ...customField, + key: customField?.key ?? customFieldConfiguration.key, + type: CustomFieldTypes.NUMBER, + value: data.value ? Number(data.value) : null, + }); + } + setIsEdit(false); + }, [customField, customFieldConfiguration.key, formState, onSubmit]); + + const title = customFieldConfiguration.label; + + const isNumberFieldValid = + formState.isValid || + (formState.value === customFieldConfiguration.defaultValue && isEmpty(initialValue)); + + const isCustomFieldValueDefined = !isEmpty(customField?.value); + + return ( + <> + + + +

{title}

+
+
+ {isLoading && ( + + )} + {!isLoading && canUpdate && ( + + + + )} +
+ + + {!isCustomFieldValueDefined && !isEdit && ( +

{NO_CUSTOM_FIELD_SET}

+ )} + {!isEdit && isCustomFieldValueDefined && ( + + + + )} + {isEdit && canUpdate && ( + + + + + + + + + {SAVE} + + + + + {CANCEL} + + + + + + )} +
+ + ); +}; + +EditComponent.displayName = 'Edit'; + +export const Edit = React.memo(EditComponent); diff --git a/x-pack/plugins/cases/public/components/custom_fields/number/get_eui_table_column.test.tsx b/x-pack/plugins/cases/public/components/custom_fields/number/get_eui_table_column.test.tsx new file mode 100644 index 0000000000000..73e94f9335705 --- /dev/null +++ b/x-pack/plugins/cases/public/components/custom_fields/number/get_eui_table_column.test.tsx @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; + +import { screen } from '@testing-library/react'; + +import { CustomFieldTypes } from '../../../../common/types/domain'; +import type { AppMockRenderer } from '../../../common/mock'; +import { createAppMockRenderer } from '../../../common/mock'; +import { getEuiTableColumn } from './get_eui_table_column'; + +describe('getEuiTableColumn ', () => { + let appMockRender: AppMockRenderer; + + beforeEach(() => { + appMockRender = createAppMockRenderer(); + + jest.clearAllMocks(); + }); + + it('returns a name and a render function', async () => { + const label = 'MockLabel'; + + expect(getEuiTableColumn({ label })).toEqual({ + name: label, + render: expect.any(Function), + width: '150px', + 'data-test-subj': 'number-custom-field-column', + }); + }); + + it('render function renders a number column correctly', async () => { + const key = 'test_key_1'; + const value = 1234567; + const column = getEuiTableColumn({ label: 'MockLabel' }); + + appMockRender.render(
{column.render({ key, type: CustomFieldTypes.NUMBER, value })}
); + + expect(screen.getByTestId(`number-custom-field-column-view-${key}`)).toBeInTheDocument(); + expect(screen.getByTestId(`number-custom-field-column-view-${key}`)).toHaveTextContent( + String(value) + ); + }); +}); diff --git a/x-pack/plugins/cases/public/components/custom_fields/number/get_eui_table_column.tsx b/x-pack/plugins/cases/public/components/custom_fields/number/get_eui_table_column.tsx new file mode 100644 index 0000000000000..a5b68364b9758 --- /dev/null +++ b/x-pack/plugins/cases/public/components/custom_fields/number/get_eui_table_column.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 React from 'react'; + +import type { CaseCustomField } from '../../../../common/types/domain'; +import type { CustomFieldEuiTableColumn } from '../types'; + +export const getEuiTableColumn = ({ label }: { label: string }): CustomFieldEuiTableColumn => ({ + name: label, + width: '150px', + render: (customField: CaseCustomField) => { + return ( +

+ {customField.value} +

+ ); + }, + 'data-test-subj': 'number-custom-field-column', +}); diff --git a/x-pack/plugins/cases/public/components/custom_fields/number/view.test.tsx b/x-pack/plugins/cases/public/components/custom_fields/number/view.test.tsx new file mode 100644 index 0000000000000..cdcc3cdacf534 --- /dev/null +++ b/x-pack/plugins/cases/public/components/custom_fields/number/view.test.tsx @@ -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 React from 'react'; +import { render, screen } from '@testing-library/react'; +import { CustomFieldTypes } from '../../../../common/types/domain'; +import { View } from './view'; + +describe('View ', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + const customField = { + type: CustomFieldTypes.NUMBER as const, + key: 'test_key_1', + value: 123 as number, + }; + + it('renders correctly', async () => { + render(); + + expect(screen.getByText('123')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/cases/public/components/custom_fields/number/view.tsx b/x-pack/plugins/cases/public/components/custom_fields/number/view.tsx new file mode 100644 index 0000000000000..542ea92def998 --- /dev/null +++ b/x-pack/plugins/cases/public/components/custom_fields/number/view.tsx @@ -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 React from 'react'; + +import { EuiText } from '@elastic/eui'; +import type { CaseCustomFieldNumber } from '../../../../common/types/domain'; +import type { CustomFieldType } from '../types'; + +const ViewComponent: CustomFieldType['View'] = ({ customField }) => { + const value = customField?.value ?? '-'; + + return ( + + {value} + + ); +}; + +ViewComponent.displayName = 'View'; + +export const View = React.memo(ViewComponent); diff --git a/x-pack/plugins/cases/public/components/custom_fields/text/configure_text_field.ts b/x-pack/plugins/cases/public/components/custom_fields/text/configure_text_field.ts index c0f50820d45f3..0f1595135f9b8 100644 --- a/x-pack/plugins/cases/public/components/custom_fields/text/configure_text_field.ts +++ b/x-pack/plugins/cases/public/components/custom_fields/text/configure_text_field.ts @@ -25,5 +25,6 @@ export const configureTextCustomFieldFactory: CustomFieldFactory (value == null ? '' : String(value)), + convertNullToEmpty: (value: string | number | boolean | null) => + value == null ? '' : String(value), }); 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 5f1a91765193f..22bafbb80f92f 100644 --- a/x-pack/plugins/cases/public/components/custom_fields/translations.ts +++ b/x-pack/plugins/cases/public/components/custom_fields/translations.ts @@ -51,6 +51,10 @@ export const TOGGLE_LABEL = i18n.translate('xpack.cases.customFields.toggleLabel defaultMessage: 'Toggle', }); +export const NUMBER_LABEL = i18n.translate('xpack.cases.customFields.textLabel', { + defaultMessage: 'Number', +}); + export const FIELD_TYPE = i18n.translate('xpack.cases.customFields.fieldType', { defaultMessage: 'Field type', }); diff --git a/x-pack/plugins/cases/public/components/custom_fields/types.ts b/x-pack/plugins/cases/public/components/custom_fields/types.ts index 70caeabd8edd2..ca63caef38748 100644 --- a/x-pack/plugins/cases/public/components/custom_fields/types.ts +++ b/x-pack/plugins/cases/public/components/custom_fields/types.ts @@ -55,7 +55,7 @@ export type CustomFieldFactory = () => { build: () => CustomFieldType; filterOptions?: CustomFieldFactoryFilterOption[]; getDefaultValue?: () => string | boolean | null; - convertNullToEmpty?: (value: string | boolean | null) => string; + convertNullToEmpty?: (value: string | number | boolean | null) => string; }; export type CustomFieldBuilderMap = { diff --git a/x-pack/plugins/cases/public/components/custom_fields/utils.test.ts b/x-pack/plugins/cases/public/components/custom_fields/utils.test.ts index 5a21319645836..61a77fc941451 100644 --- a/x-pack/plugins/cases/public/components/custom_fields/utils.test.ts +++ b/x-pack/plugins/cases/public/components/custom_fields/utils.test.ts @@ -97,5 +97,40 @@ describe('utils ', () => { } `); }); + + it('serializes the data correctly if the default value is integer number', async () => { + const customField = { + key: 'my_test_key', + type: CustomFieldTypes.NUMBER, + required: true, + defaultValue: 1, + } as CustomFieldConfiguration; + + expect(customFieldSerializer(customField)).toMatchInlineSnapshot(` + Object { + "defaultValue": 1, + "key": "my_test_key", + "required": true, + "type": "number", + } + `); + }); + + it('serializes the data correctly if the default value is float number', async () => { + const customField = { + key: 'my_test_key', + type: CustomFieldTypes.NUMBER, + required: true, + defaultValue: 1.5, + } as CustomFieldConfiguration; + + expect(customFieldSerializer(customField)).toMatchInlineSnapshot(` + Object { + "key": "my_test_key", + "required": true, + "type": "number", + } + `); + }); }); }); diff --git a/x-pack/plugins/cases/public/components/custom_fields/utils.ts b/x-pack/plugins/cases/public/components/custom_fields/utils.ts index 3842b75b5a7ea..96438a9337265 100644 --- a/x-pack/plugins/cases/public/components/custom_fields/utils.ts +++ b/x-pack/plugins/cases/public/components/custom_fields/utils.ts @@ -8,6 +8,7 @@ import { isEmptyString } from '@kbn/es-ui-shared-plugin/static/validators/string'; import { isString } from 'lodash'; import type { CustomFieldConfiguration } from '../../../common/types/domain'; +import { CustomFieldTypes } from '../../../common/types/domain'; export const customFieldSerializer = ( field: CustomFieldConfiguration @@ -18,5 +19,13 @@ export const customFieldSerializer = ( return otherProperties; } + if (field.type === CustomFieldTypes.NUMBER) { + if (defaultValue !== null && Number.isSafeInteger(Number(defaultValue))) { + return { ...field, defaultValue: Number(defaultValue) }; + } else { + return otherProperties; + } + } + return field; }; diff --git a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx index 6e56126708034..81758ea1076c7 100644 --- a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx +++ b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx @@ -61,7 +61,8 @@ const defaultProps = { editorRef, }; -describe('EditableMarkdown', () => { +// FLAKY: https://github.com/elastic/kibana/issues/171177 +describe.skip('EditableMarkdown', () => { let appMockRender: AppMockRenderer; beforeEach(() => { diff --git a/x-pack/plugins/cases/public/components/templates/form.test.tsx b/x-pack/plugins/cases/public/components/templates/form.test.tsx index bf5f66aaa3e21..349457c2be98f 100644 --- a/x-pack/plugins/cases/public/components/templates/form.test.tsx +++ b/x-pack/plugins/cases/public/components/templates/form.test.tsx @@ -589,11 +589,14 @@ describe('TemplateForm', () => { expect( await within(customFieldsElement).findAllByTestId('form-optional-field-label') ).toHaveLength( - customFieldsConfigurationMock.filter((field) => field.type === CustomFieldTypes.TEXT).length + customFieldsConfigurationMock.filter( + (field) => field.type === CustomFieldTypes.TEXT || field.type === CustomFieldTypes.NUMBER + ).length ); const textField = customFieldsConfigurationMock[0]; const toggleField = customFieldsConfigurationMock[3]; + const numberField = customFieldsConfigurationMock[4]; const textCustomField = await screen.findByTestId( `${textField.key}-${textField.type}-create-custom-field` @@ -608,6 +611,15 @@ describe('TemplateForm', () => { await screen.findByTestId(`${toggleField.key}-${toggleField.type}-create-custom-field`) ); + const numberCustomField = await screen.findByTestId( + `${numberField.key}-${numberField.type}-create-custom-field` + ); + + await user.clear(numberCustomField); + + await user.click(numberCustomField); + await user.paste('765'); + const submitSpy = jest.spyOn(formState!, 'submit'); await user.click(screen.getByText('testSubmit')); @@ -644,6 +656,16 @@ describe('TemplateForm', () => { type: 'toggle', value: true, }, + { + key: 'test_key_5', + type: 'number', + value: 1234, + }, + { + key: 'test_key_6', + type: 'number', + value: true, + }, ], settings: { syncAlerts: true, diff --git a/x-pack/plugins/cases/public/components/templates/form_fields.test.tsx b/x-pack/plugins/cases/public/components/templates/form_fields.test.tsx index 75cfa58e8d5f8..48c6f956ccc7c 100644 --- a/x-pack/plugins/cases/public/components/templates/form_fields.test.tsx +++ b/x-pack/plugins/cases/public/components/templates/form_fields.test.tsx @@ -311,6 +311,7 @@ describe('form fields', () => { const textField = customFieldsConfigurationMock[0]; const toggleField = customFieldsConfigurationMock[1]; + const numberField = customFieldsConfigurationMock[4]; const textCustomField = await screen.findByTestId( `${textField.key}-${textField.type}-create-custom-field` @@ -324,6 +325,14 @@ describe('form fields', () => { await screen.findByTestId(`${toggleField.key}-${toggleField.type}-create-custom-field`) ); + const numberCustomField = await screen.findByTestId( + `${numberField.key}-${numberField.type}-create-custom-field` + ); + + await userEvent.clear(numberCustomField); + await userEvent.click(numberCustomField); + await userEvent.paste('987'); + await userEvent.click(screen.getByText('Submit')); await waitFor(() => { @@ -336,6 +345,7 @@ describe('form fields', () => { test_key_1: 'My text test value 1', test_key_2: false, test_key_4: false, + test_key_5: '987', }, syncAlerts: true, templateTags: [], diff --git a/x-pack/plugins/cases/public/components/utils.test.ts b/x-pack/plugins/cases/public/components/utils.test.ts index 005f15b78b3d7..f10590cc9a358 100644 --- a/x-pack/plugins/cases/public/components/utils.test.ts +++ b/x-pack/plugins/cases/public/components/utils.test.ts @@ -523,19 +523,46 @@ describe('Utils', () => { }); it('returns the string when the value is a non-empty string', async () => { - expect(convertCustomFieldValue('my text value')).toMatchInlineSnapshot(`"my text value"`); + expect( + convertCustomFieldValue({ value: 'my text value', type: CustomFieldTypes.TEXT }) + ).toMatchInlineSnapshot(`"my text value"`); }); it('returns null when value is empty string', async () => { - expect(convertCustomFieldValue('')).toMatchInlineSnapshot('null'); + expect( + convertCustomFieldValue({ value: '', type: CustomFieldTypes.TEXT }) + ).toMatchInlineSnapshot('null'); }); it('returns value as it is when value is true', async () => { - expect(convertCustomFieldValue(true)).toMatchInlineSnapshot('true'); + expect( + convertCustomFieldValue({ value: true, type: CustomFieldTypes.TOGGLE }) + ).toMatchInlineSnapshot('true'); }); it('returns value as it is when value is false', async () => { - expect(convertCustomFieldValue(false)).toMatchInlineSnapshot('false'); + expect( + convertCustomFieldValue({ value: false, type: CustomFieldTypes.TOGGLE }) + ).toMatchInlineSnapshot('false'); + }); + it('returns value as integer number when value is integer string and type is number', () => { + expect(convertCustomFieldValue({ value: '123', type: CustomFieldTypes.NUMBER })).toEqual(123); + }); + + it('returns value as null when value is float string and type is number', () => { + expect(convertCustomFieldValue({ value: '0.5', type: CustomFieldTypes.NUMBER })).toEqual( + null + ); + }); + + it('returns value as null when value is null and type is number', () => { + expect(convertCustomFieldValue({ value: null, type: CustomFieldTypes.NUMBER })).toEqual(null); + }); + + it('returns value as null when value is characters string and type is number', () => { + expect(convertCustomFieldValue({ value: 'fdgdg', type: CustomFieldTypes.NUMBER })).toEqual( + null + ); }); }); @@ -575,6 +602,16 @@ describe('Utils', () => { "type": "toggle", "value": null, }, + Object { + "key": "test_key_5", + "type": "number", + "value": 1234, + }, + Object { + "key": "test_key_6", + "type": "number", + "value": null, + }, Object { "key": "my_test_key", "type": "text", @@ -598,6 +635,8 @@ describe('Utils', () => { { ...customFieldsMock[1] }, { ...customFieldsMock[2] }, { ...customFieldsMock[3] }, + { ...customFieldsMock[4] }, + { ...customFieldsMock[5] }, ], ` Array [ @@ -626,6 +665,16 @@ describe('Utils', () => { "type": "toggle", "value": null, }, + Object { + "key": "test_key_5", + "type": "number", + "value": 1234, + }, + Object { + "key": "test_key_6", + "type": "number", + "value": null, + }, ] ` ); @@ -669,6 +718,19 @@ describe('Utils', () => { "required": false, "type": "toggle", }, + Object { + "defaultValue": 123, + "key": "test_key_5", + "label": "My test label 5", + "required": true, + "type": "number", + }, + Object { + "key": "test_key_6", + "label": "My test label 6", + "required": false, + "type": "number", + }, Object { "key": "my_test_key", "label": "my_test_label", @@ -693,6 +755,8 @@ describe('Utils', () => { { ...customFieldsConfigurationMock[1] }, { ...customFieldsConfigurationMock[2] }, { ...customFieldsConfigurationMock[3] }, + { ...customFieldsConfigurationMock[4] }, + { ...customFieldsConfigurationMock[5] }, ], ` Array [ @@ -722,6 +786,19 @@ describe('Utils', () => { "required": false, "type": "toggle", }, + Object { + "defaultValue": 123, + "key": "test_key_5", + "label": "My test label 5", + "required": true, + "type": "number", + }, + Object { + "key": "test_key_6", + "label": "My test label 6", + "required": false, + "type": "number", + }, ] ` ); diff --git a/x-pack/plugins/cases/public/components/utils.ts b/x-pack/plugins/cases/public/components/utils.ts index 7e1aa54554f50..bcc6be9a7ae9e 100644 --- a/x-pack/plugins/cases/public/components/utils.ts +++ b/x-pack/plugins/cases/public/components/utils.ts @@ -13,7 +13,7 @@ import type { } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import type { UserProfileWithAvatar } from '@kbn/user-profile-components'; import type { ConnectorTypeFields } from '../../common/types/domain'; -import { ConnectorTypes } from '../../common/types/domain'; +import { ConnectorTypes, CustomFieldTypes } from '../../common/types/domain'; import type { CasesPublicStartDependencies } from '../types'; import { connectorValidator as swimlaneConnectorValidator } from './connectors/swimlane/validator'; import type { CaseActionConnector } from './types'; @@ -234,11 +234,25 @@ export const parseCaseUsers = ({ return { userProfiles, reporterAsArray }; }; -export const convertCustomFieldValue = (value: string | boolean) => { +export const convertCustomFieldValue = ({ + value, + type, +}: { + value: string | number | boolean | null; + type: CustomFieldTypes; +}) => { if (typeof value === 'string' && isEmpty(value)) { return null; } + if (type === CustomFieldTypes.NUMBER) { + if (value !== null && Number.isSafeInteger(Number(value))) { + return Number(value); + } else { + return null; + } + } + return value; }; @@ -288,7 +302,7 @@ export const customFieldsFormDeserializer = ( }; export const customFieldsFormSerializer = ( - customFields: Record, + customFields: Record, selectedCustomFieldsConfiguration: CasesConfigurationUI['customFields'] ): CaseUI['customFields'] => { const transformedCustomFields: CaseUI['customFields'] = []; @@ -303,7 +317,7 @@ export const customFieldsFormSerializer = ( transformedCustomFields.push({ key: configCustomField.key, type: configCustomField.type, - value: convertCustomFieldValue(value), + value: convertCustomFieldValue({ value, type: configCustomField.type }), } as CaseUICustomField); } } diff --git a/x-pack/plugins/cases/public/containers/mock.ts b/x-pack/plugins/cases/public/containers/mock.ts index 8d2feca6b9be0..c3cee2d60d2b0 100644 --- a/x-pack/plugins/cases/public/containers/mock.ts +++ b/x-pack/plugins/cases/public/containers/mock.ts @@ -1158,6 +1158,8 @@ export const customFieldsMock: CaseUICustomField[] = [ { type: CustomFieldTypes.TOGGLE, key: 'test_key_2', value: true }, { type: CustomFieldTypes.TEXT, key: 'test_key_3', value: null }, { type: CustomFieldTypes.TOGGLE, key: 'test_key_4', value: null }, + { type: CustomFieldTypes.NUMBER, key: 'test_key_5', value: 1234 }, + { type: CustomFieldTypes.NUMBER, key: 'test_key_6', value: null }, ]; export const customFieldsConfigurationMock: CasesConfigurationUICustomField[] = [ @@ -1177,6 +1179,19 @@ export const customFieldsConfigurationMock: CasesConfigurationUICustomField[] = }, { type: CustomFieldTypes.TEXT, key: 'test_key_3', label: 'My test label 3', required: false }, { type: CustomFieldTypes.TOGGLE, key: 'test_key_4', label: 'My test label 4', required: false }, + { + type: CustomFieldTypes.NUMBER, + key: 'test_key_5', + label: 'My test label 5', + required: true, + defaultValue: 123, + }, + { + type: CustomFieldTypes.NUMBER, + key: 'test_key_6', + label: 'My test label 6', + required: false, + }, ]; export const templatesConfigurationMock: CasesConfigurationUITemplate[] = [ diff --git a/x-pack/plugins/cases/public/containers/use_replace_custom_field.tsx b/x-pack/plugins/cases/public/containers/use_replace_custom_field.tsx index 5d2969f6e6d44..f1d0b87ff07e8 100644 --- a/x-pack/plugins/cases/public/containers/use_replace_custom_field.tsx +++ b/x-pack/plugins/cases/public/containers/use_replace_custom_field.tsx @@ -16,7 +16,7 @@ import * as i18n from './translations'; interface ReplaceCustomField { caseId: string; customFieldId: string; - customFieldValue: string | boolean | null; + customFieldValue: string | number | boolean | null; caseVersion: string; } diff --git a/x-pack/plugins/cases/server/client/utils.test.ts b/x-pack/plugins/cases/server/client/utils.test.ts index eb7aaea6d6938..680887b82c653 100644 --- a/x-pack/plugins/cases/server/client/utils.test.ts +++ b/x-pack/plugins/cases/server/client/utils.test.ts @@ -906,7 +906,7 @@ describe('utils', () => { ...customFieldsConfiguration, { key: 'fourth_key', - type: 'number', + type: 'symbol', label: 'Number field', required: true, }, diff --git a/x-pack/plugins/cases/server/common/types/configure.ts b/x-pack/plugins/cases/server/common/types/configure.ts index faf2517fbe173..27e66ba76eb02 100644 --- a/x-pack/plugins/cases/server/common/types/configure.ts +++ b/x-pack/plugins/cases/server/common/types/configure.ts @@ -39,7 +39,7 @@ type PersistedCustomFieldsConfiguration = Array<{ type: string; label: string; required: boolean; - defaultValue?: string | boolean | null; + defaultValue?: string | number | boolean | null; }>; type PersistedTemplatesConfiguration = Array<{ diff --git a/x-pack/plugins/cases/server/connectors/cases/constants.ts b/x-pack/plugins/cases/server/connectors/cases/constants.ts index fafd1a3e0eaeb..f1d0e548e1f3a 100644 --- a/x-pack/plugins/cases/server/connectors/cases/constants.ts +++ b/x-pack/plugins/cases/server/connectors/cases/constants.ts @@ -12,8 +12,11 @@ export const MAX_OPEN_CASES = 10; export const DEFAULT_MAX_OPEN_CASES = 5; export const INITIAL_ORACLE_RECORD_COUNTER = 1; -export const VALUES_FOR_CUSTOM_FIELDS_MISSING_DEFAULTS: Record = - { - [CustomFieldTypes.TEXT]: 'N/A', - [CustomFieldTypes.TOGGLE]: false, - }; +export const VALUES_FOR_CUSTOM_FIELDS_MISSING_DEFAULTS: Record< + CustomFieldTypes, + string | boolean | number +> = { + [CustomFieldTypes.TEXT]: 'N/A', + [CustomFieldTypes.TOGGLE]: false, + [CustomFieldTypes.NUMBER]: 0, +}; diff --git a/x-pack/plugins/cases/server/custom_fields/factory.ts b/x-pack/plugins/cases/server/custom_fields/factory.ts index d9e1bc86671fe..3b42dcfd6eddb 100644 --- a/x-pack/plugins/cases/server/custom_fields/factory.ts +++ b/x-pack/plugins/cases/server/custom_fields/factory.ts @@ -9,10 +9,12 @@ import { CustomFieldTypes } from '../../common/types/domain'; import type { ICasesCustomField, CasesCustomFieldsMap } from './types'; import { getCasesTextCustomField } from './text'; import { getCasesToggleCustomField } from './toggle'; +import { getCasesNumberCustomField } from './number'; const mapping: Record = { [CustomFieldTypes.TEXT]: getCasesTextCustomField(), [CustomFieldTypes.TOGGLE]: getCasesToggleCustomField(), + [CustomFieldTypes.NUMBER]: getCasesNumberCustomField(), }; export const casesCustomFields: CasesCustomFieldsMap = { diff --git a/x-pack/plugins/cases/server/custom_fields/number.ts b/x-pack/plugins/cases/server/custom_fields/number.ts new file mode 100644 index 0000000000000..f036a01cbe1b8 --- /dev/null +++ b/x-pack/plugins/cases/server/custom_fields/number.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 Boom from '@hapi/boom'; + +export const getCasesNumberCustomField = () => ({ + isFilterable: false, + isSortable: false, + savedObjectMappingType: 'long', + validateFilteringValues: (values: Array) => { + values.forEach((value) => { + if (value !== null && !Number.isSafeInteger(value)) { + throw Boom.badRequest('Unsupported filtering value for custom field of type number.'); + } + }); + }, +}); diff --git a/x-pack/plugins/cases/server/plugin.ts b/x-pack/plugins/cases/server/plugin.ts index fa172b48520a7..b40089ff75050 100644 --- a/x-pack/plugins/cases/server/plugin.ts +++ b/x-pack/plugins/cases/server/plugin.ts @@ -122,9 +122,14 @@ export class CasePlugin const router = core.http.createRouter(); const telemetryUsageCounter = plugins.usageCollection?.createUsageCounter(APP_ID); + const isServerless = plugins.cloud?.isServerlessEnabled; + registerRoutes({ router, - routes: [...getExternalRoutes(), ...getInternalRoutes(this.userProfileService)], + routes: [ + ...getExternalRoutes({ isServerless }), + ...getInternalRoutes(this.userProfileService), + ], logger: this.logger, kibanaVersion: this.kibanaVersion, telemetryUsageCounter, diff --git a/x-pack/plugins/cases/server/routes/api/cases/get_case.test.ts b/x-pack/plugins/cases/server/routes/api/cases/get_case.test.ts new file mode 100644 index 0000000000000..45ee5e8f47163 --- /dev/null +++ b/x-pack/plugins/cases/server/routes/api/cases/get_case.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 { createCasesClientMock } from '../../../client/mocks'; +import { getCaseRoute } from './get_case'; +import { httpServerMock, loggingSystemMock } from '@kbn/core/server/mocks'; + +describe('getCaseRoute', () => { + const casesClientMock = createCasesClientMock(); + const logger = loggingSystemMock.createLogger(); + const response = httpServerMock.createResponseFactory(); + const kibanaVersion = '8.17'; + const context = { cases: { getCasesClient: jest.fn().mockResolvedValue(casesClientMock) } }; + + it('throws a bad request if the includeComments is set in serverless', async () => { + const router = getCaseRoute({ isServerless: true }); + const request = httpServerMock.createKibanaRequest({ + path: '/api/cases/{case_id}/?includeComments=true', + query: { includeComments: true }, + params: { case_id: 'foo' }, + }); + + await expect( + // @ts-expect-error: no need to create the context + router.handler({ response, request, logger, kibanaVersion, context }) + ).rejects.toThrowErrorMatchingInlineSnapshot(` + "Failed to retrieve case in route case id: foo + include comments: true: Error: includeComments is not supported" + `); + }); + + it('does not throw a bad request if the includeComments is set in non-serverless', async () => { + const router = getCaseRoute({ isServerless: false }); + const request = httpServerMock.createKibanaRequest({ + path: '/api/cases/{case_id}/?includeComments=true', + query: { includeComments: true }, + params: { case_id: 'foo' }, + }); + + await expect( + // @ts-expect-error: no need to create the context + router.handler({ response, request, logger, kibanaVersion, context }) + ).resolves.not.toThrow(); + }); + + it('does not throw a bad request if the includeComments is not set in serverless', async () => { + const router = getCaseRoute({ isServerless: true }); + const request = httpServerMock.createKibanaRequest({ + path: '/api/cases/{case_id}', + params: { case_id: 'foo' }, + }); + + await expect( + // @ts-expect-error: no need to create the context + router.handler({ response, request, logger, kibanaVersion, context }) + ).resolves.not.toThrow(); + }); +}); diff --git a/x-pack/plugins/cases/server/routes/api/cases/get_case.ts b/x-pack/plugins/cases/server/routes/api/cases/get_case.ts index 831a7be129f70..158360ff7b23f 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/get_case.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/get_case.ts @@ -5,6 +5,7 @@ * 2.0. */ +import Boom from '@hapi/boom'; import { schema } from '@kbn/config-schema'; import type { caseApiV1 } from '../../../../common/types/api'; @@ -26,53 +27,58 @@ const params = { }), }; -export const getCaseRoute = createCasesRoute({ - method: 'get', - path: CASE_DETAILS_URL, - params, - routerOptions: { - access: 'public', - summary: `Get a case`, - tags: ['oas-tag:cases'], - }, - handler: async ({ context, request, response, logger, kibanaVersion }) => { - try { - const isIncludeCommentsParamProvidedByTheUser = - request.url.searchParams.has('includeComments'); +export const getCaseRoute = ({ isServerless }: { isServerless?: boolean }) => + createCasesRoute({ + method: 'get', + path: CASE_DETAILS_URL, + params, + routerOptions: { + access: 'public', + summary: `Get a case`, + tags: ['oas-tag:cases'], + }, + handler: async ({ context, request, response, logger, kibanaVersion }) => { + try { + const isIncludeCommentsParamProvidedByTheUser = + request.url.searchParams.has('includeComments'); - if (isIncludeCommentsParamProvidedByTheUser) { - logDeprecatedEndpoint( - logger, - request.headers, - `The query parameter 'includeComments' of the get case API '${CASE_DETAILS_URL}' is deprecated` - ); - } + if (isServerless && isIncludeCommentsParamProvidedByTheUser) { + throw Boom.badRequest('includeComments is not supported'); + } - const caseContext = await context.cases; - const casesClient = await caseContext.getCasesClient(); - const id = request.params.case_id; + if (isIncludeCommentsParamProvidedByTheUser) { + logDeprecatedEndpoint( + logger, + request.headers, + `The query parameter 'includeComments' of the get case API '${CASE_DETAILS_URL}' is deprecated` + ); + } - const res: caseDomainV1.Case = await casesClient.cases.get({ - id, - includeComments: request.query.includeComments, - }); + const caseContext = await context.cases; + const casesClient = await caseContext.getCasesClient(); + const id = request.params.case_id; - return response.ok({ - ...(isIncludeCommentsParamProvidedByTheUser && { - headers: { - ...getWarningHeader(kibanaVersion, 'Deprecated query parameter includeComments'), - }, - }), - body: res, - }); - } catch (error) { - throw createCaseError({ - message: `Failed to retrieve case in route case id: ${request.params.case_id} \ninclude comments: ${request.query.includeComments}: ${error}`, - error, - }); - } - }, -}); + const res: caseDomainV1.Case = await casesClient.cases.get({ + id, + includeComments: request.query.includeComments, + }); + + return response.ok({ + ...(isIncludeCommentsParamProvidedByTheUser && { + headers: { + ...getWarningHeader(kibanaVersion, 'Deprecated query parameter includeComments'), + }, + }), + body: res, + }); + } catch (error) { + throw createCaseError({ + message: `Failed to retrieve case in route case id: ${request.params.case_id} \ninclude comments: ${request.query.includeComments}: ${error}`, + error, + }); + } + }, + }); export const resolveCaseRoute = createCasesRoute({ method: 'get', diff --git a/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.test.ts b/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.test.ts new file mode 100644 index 0000000000000..9687e73d1f7c8 --- /dev/null +++ b/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getAllCommentsRoute } from './get_all_comment'; + +describe('getAllCommentsRoute', () => { + it('marks the endpoint internal in serverless', async () => { + const router = getAllCommentsRoute({ isServerless: true }); + + expect(router.routerOptions?.access).toBe('internal'); + }); + + it('marks the endpoint public in non-serverless', async () => { + const router = getAllCommentsRoute({ isServerless: false }); + + expect(router.routerOptions?.access).toBe('public'); + }); +}); diff --git a/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts index 6e8ac79bffec9..0f84ed29dce29 100644 --- a/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts +++ b/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts @@ -15,41 +15,42 @@ import type { attachmentDomainV1 } from '../../../../common/types/domain'; /** * @deprecated since version 8.1.0 */ -export const getAllCommentsRoute = createCasesRoute({ - method: 'get', - path: CASE_COMMENTS_URL, - params: { - params: schema.object({ - case_id: schema.string(), - }), - }, - options: { - deprecated: true, - }, - routerOptions: { - access: 'public', - summary: `Gets all case comments`, - tags: ['oas-tag:cases'], - // description: 'You must have `read` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the cases with the comments you\'re seeking.', - // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} - deprecated: true, - }, - handler: async ({ context, request, response }) => { - try { - const caseContext = await context.cases; - const client = await caseContext.getCasesClient(); - const res: attachmentDomainV1.Attachments = await client.attachments.getAll({ - caseID: request.params.case_id, - }); +export const getAllCommentsRoute = ({ isServerless }: { isServerless?: boolean }) => + createCasesRoute({ + method: 'get', + path: CASE_COMMENTS_URL, + params: { + params: schema.object({ + case_id: schema.string(), + }), + }, + options: { + deprecated: true, + }, + routerOptions: { + access: isServerless ? 'internal' : 'public', + summary: `Gets all case comments`, + tags: ['oas-tag:cases'], + // description: 'You must have `read` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the cases with the comments you\'re seeking.', + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} + deprecated: true, + }, + handler: async ({ context, request, response }) => { + try { + const caseContext = await context.cases; + const client = await caseContext.getCasesClient(); + const res: attachmentDomainV1.Attachments = await client.attachments.getAll({ + caseID: request.params.case_id, + }); - return response.ok({ - body: res, - }); - } catch (error) { - throw createCaseError({ - message: `Failed to get all comments in route case id: ${request.params.case_id}: ${error}`, - error, - }); - } - }, -}); + return response.ok({ + body: res, + }); + } catch (error) { + throw createCaseError({ + message: `Failed to get all comments in route case id: ${request.params.case_id}: ${error}`, + error, + }); + } + }, + }); diff --git a/x-pack/plugins/cases/server/routes/api/get_external_routes.ts b/x-pack/plugins/cases/server/routes/api/get_external_routes.ts index bd990deefbdfa..4412d0b695079 100644 --- a/x-pack/plugins/cases/server/routes/api/get_external_routes.ts +++ b/x-pack/plugins/cases/server/routes/api/get_external_routes.ts @@ -31,18 +31,18 @@ import { postCaseConfigureRoute } from './configure/post_configure'; import { getAllAlertsAttachedToCaseRoute } from './comments/get_alerts'; import { findUserActionsRoute } from './user_actions/find_user_actions'; -export const getExternalRoutes = () => +export const getExternalRoutes = ({ isServerless }: { isServerless?: boolean }) => [ deleteCaseRoute, findCaseRoute, - getCaseRoute, + getCaseRoute({ isServerless }), resolveCaseRoute, patchCaseRoute, postCaseRoute, pushCaseRoute, findUserActionsRoute, - getUserActionsRoute, - getStatusRoute, + getUserActionsRoute({ isServerless }), + getStatusRoute({ isServerless }), getCasesByAlertIdRoute, getReportersRoute, getTagsRoute, @@ -50,7 +50,7 @@ export const getExternalRoutes = () => deleteAllCommentsRoute, findCommentsRoute, getCommentRoute, - getAllCommentsRoute, + getAllCommentsRoute({ isServerless }), patchCommentRoute, postCommentRoute, getCaseConfigureRoute, diff --git a/x-pack/plugins/cases/server/routes/api/stats/get_status.test.ts b/x-pack/plugins/cases/server/routes/api/stats/get_status.test.ts new file mode 100644 index 0000000000000..9376a46b76808 --- /dev/null +++ b/x-pack/plugins/cases/server/routes/api/stats/get_status.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getStatusRoute } from './get_status'; + +describe('getStatusRoute', () => { + it('marks the endpoint internal in serverless', async () => { + const router = getStatusRoute({ isServerless: true }); + + expect(router.routerOptions?.access).toBe('internal'); + }); + + it('marks the endpoint public in non-serverless', async () => { + const router = getStatusRoute({ isServerless: false }); + + expect(router.routerOptions?.access).toBe('public'); + }); +}); diff --git a/x-pack/plugins/cases/server/routes/api/stats/get_status.ts b/x-pack/plugins/cases/server/routes/api/stats/get_status.ts index dce369e4a0f45..0889644f6a80a 100644 --- a/x-pack/plugins/cases/server/routes/api/stats/get_status.ts +++ b/x-pack/plugins/cases/server/routes/api/stats/get_status.ts @@ -15,37 +15,38 @@ import type { statsApiV1 } from '../../../../common/types/api'; /** * @deprecated since version 8.1.0 */ -export const getStatusRoute: CaseRoute = createCasesRoute({ - method: 'get', - path: CASE_STATUS_URL, - options: { deprecated: true }, - routerOptions: { - access: 'public', - summary: `Get case status summary`, - tags: ['oas-tag:cases'], - description: - 'Returns the number of cases that are open, closed, and in progress in the default space.', - // You must have `read` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the cases you're seeking. - // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} - deprecated: true, - }, - handler: async ({ context, request, response }) => { - try { - const caseContext = await context.cases; - const client = await caseContext.getCasesClient(); +export const getStatusRoute = ({ isServerless }: { isServerless?: boolean }): CaseRoute => + createCasesRoute({ + method: 'get', + path: CASE_STATUS_URL, + options: { deprecated: true }, + routerOptions: { + access: isServerless ? 'internal' : 'public', + summary: `Get case status summary`, + tags: ['oas-tag:cases'], + description: + 'Returns the number of cases that are open, closed, and in progress in the default space.', + // You must have `read` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the cases you're seeking. + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} + deprecated: true, + }, + handler: async ({ context, request, response }) => { + try { + const caseContext = await context.cases; + const client = await caseContext.getCasesClient(); - const res: statsApiV1.CasesStatusResponse = await client.metrics.getStatusTotalsByType( - request.query as statsApiV1.CasesStatusRequest - ); + const res: statsApiV1.CasesStatusResponse = await client.metrics.getStatusTotalsByType( + request.query as statsApiV1.CasesStatusRequest + ); - return response.ok({ - body: res, - }); - } catch (error) { - throw createCaseError({ - message: `Failed to get status stats in route: ${error}`, - error, - }); - } - }, -}); + return response.ok({ + body: res, + }); + } catch (error) { + throw createCaseError({ + message: `Failed to get status stats in route: ${error}`, + error, + }); + } + }, + }); diff --git a/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.test.ts b/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.test.ts new file mode 100644 index 0000000000000..d99b90c29bbb4 --- /dev/null +++ b/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getUserActionsRoute } from './get_all_user_actions'; + +describe('getUserActionsRoute', () => { + it('marks the endpoint internal in serverless', async () => { + const router = getUserActionsRoute({ isServerless: true }); + + expect(router.routerOptions?.access).toBe('internal'); + }); + + it('marks the endpoint public in non-serverless', async () => { + const router = getUserActionsRoute({ isServerless: false }); + + expect(router.routerOptions?.access).toBe('public'); + }); +}); diff --git a/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts b/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts index 17fe0dcdb9012..19d7f1f8956ac 100644 --- a/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts +++ b/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts @@ -15,41 +15,42 @@ import { createCasesRoute } from '../create_cases_route'; /** * @deprecated since version 8.1.0 */ -export const getUserActionsRoute = createCasesRoute({ - method: 'get', - path: CASE_USER_ACTIONS_URL, - params: { - params: schema.object({ - case_id: schema.string(), - }), - }, - options: { deprecated: true }, - routerOptions: { - access: 'public', - summary: 'Get case activity', - description: `Returns all user activity for a case.`, - // You must have `read` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the case you're seeking. - tags: ['oas-tag:cases'], - // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} - deprecated: true, - }, - handler: async ({ context, request, response }) => { - try { - const caseContext = await context.cases; - const casesClient = await caseContext.getCasesClient(); - const caseId = request.params.case_id; +export const getUserActionsRoute = ({ isServerless }: { isServerless?: boolean }) => + createCasesRoute({ + method: 'get', + path: CASE_USER_ACTIONS_URL, + params: { + params: schema.object({ + case_id: schema.string(), + }), + }, + options: { deprecated: true }, + routerOptions: { + access: isServerless ? 'internal' : 'public', + summary: 'Get case activity', + description: `Returns all user activity for a case.`, + // You must have `read` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the case you're seeking. + tags: ['oas-tag:cases'], + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} + deprecated: true, + }, + handler: async ({ context, request, response }) => { + try { + const caseContext = await context.cases; + const casesClient = await caseContext.getCasesClient(); + const caseId = request.params.case_id; - const res: userActionApiV1.CaseUserActionsDeprecatedResponse = - await casesClient.userActions.getAll({ caseId }); + const res: userActionApiV1.CaseUserActionsDeprecatedResponse = + await casesClient.userActions.getAll({ caseId }); - return response.ok({ - body: res, - }); - } catch (error) { - throw createCaseError({ - message: `Failed to retrieve case user actions in route case id: ${request.params.case_id}: ${error}`, - error, - }); - } - }, -}); + return response.ok({ + body: res, + }); + } catch (error) { + throw createCaseError({ + message: `Failed to retrieve case user actions in route case id: ${request.params.case_id}: ${error}`, + error, + }); + } + }, + }); diff --git a/x-pack/plugins/cloud/common/parse_onboarding_default_solution.ts b/x-pack/plugins/cloud/common/parse_onboarding_default_solution.ts index 5b064eecce12d..483e6771394d2 100644 --- a/x-pack/plugins/cloud/common/parse_onboarding_default_solution.ts +++ b/x-pack/plugins/cloud/common/parse_onboarding_default_solution.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { OnBoardingDefaultSolution } from './types'; +import type { SolutionId } from '@kbn/core-chrome-browser'; /** * Cloud does not type the value of the "use case" that is set during onboarding for a deployment. Any string can @@ -14,12 +14,12 @@ import type { OnBoardingDefaultSolution } from './types'; * @param value The solution value set by Cloud. * @returns The default solution value for onboarding that matches Kibana naming. */ -export function parseOnboardingSolution(value?: string): OnBoardingDefaultSolution | undefined { +export function parseOnboardingSolution(value?: string): SolutionId | undefined { if (!value) return; const solutions: Array<{ cloudValue: 'search' | 'elasticsearch' | 'observability' | 'security'; - kibanaValue: OnBoardingDefaultSolution; + kibanaValue: SolutionId; }> = [ { cloudValue: 'search', diff --git a/x-pack/plugins/cloud/common/types.ts b/x-pack/plugins/cloud/common/types.ts index 0f72caf515058..b3a32270af6cc 100644 --- a/x-pack/plugins/cloud/common/types.ts +++ b/x-pack/plugins/cloud/common/types.ts @@ -5,8 +5,6 @@ * 2.0. */ -export type OnBoardingDefaultSolution = 'es' | 'oblt' | 'security'; - export interface ElasticsearchConfigType { elasticsearch_url?: string; } diff --git a/x-pack/plugins/cloud/public/types.ts b/x-pack/plugins/cloud/public/types.ts index 2a6140ba8e97e..91972b69ec4ef 100644 --- a/x-pack/plugins/cloud/public/types.ts +++ b/x-pack/plugins/cloud/public/types.ts @@ -5,8 +5,8 @@ * 2.0. */ +import type { SolutionId } from '@kbn/core-chrome-browser'; import type { FC, PropsWithChildren } from 'react'; -import type { OnBoardingDefaultSolution } from '../common'; export interface CloudStart { /** @@ -192,7 +192,7 @@ export interface CloudSetup { /** * The default solution selected during onboarding. */ - defaultSolution?: OnBoardingDefaultSolution; + defaultSolution?: SolutionId; }; /** * `true` when running on Serverless Elastic Cloud diff --git a/x-pack/plugins/cloud/server/plugin.ts b/x-pack/plugins/cloud/server/plugin.ts index 9f45b5398ac22..9821aa318e264 100644 --- a/x-pack/plugins/cloud/server/plugin.ts +++ b/x-pack/plugins/cloud/server/plugin.ts @@ -8,10 +8,10 @@ import type { Logger } from '@kbn/logging'; import type { CoreSetup, Plugin, PluginInitializerContext } from '@kbn/core/server'; import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; +import type { SolutionId } from '@kbn/core-chrome-browser'; import { registerCloudDeploymentMetadataAnalyticsContext } from '../common/register_cloud_deployment_id_analytics_context'; import type { CloudConfigType } from './config'; import { registerCloudUsageCollector } from './collectors'; -import type { OnBoardingDefaultSolution } from '../common'; import { getIsCloudEnabled } from '../common/is_cloud_enabled'; import { parseDeploymentIdFromDeploymentUrl } from '../common/parse_deployment_id_from_deployment_url'; import { decodeCloudId, DecodedCloudId } from '../common/decode_cloud_id'; @@ -108,7 +108,7 @@ export interface CloudSetup { /** * The default solution selected during onboarding. */ - defaultSolution?: OnBoardingDefaultSolution; + defaultSolution?: SolutionId; }; /** * `true` when running on Serverless Elastic Cloud diff --git a/x-pack/plugins/cloud/tsconfig.json b/x-pack/plugins/cloud/tsconfig.json index fdf2ad6db58cd..dd25064897758 100644 --- a/x-pack/plugins/cloud/tsconfig.json +++ b/x-pack/plugins/cloud/tsconfig.json @@ -12,6 +12,7 @@ ], "kbn_references": [ "@kbn/core", + "@kbn/core-chrome-browser", "@kbn/usage-collection-plugin", "@kbn/config-schema", "@kbn/logging-mocks", diff --git a/x-pack/plugins/cloud_defend/server/routes/policies/policies.ts b/x-pack/plugins/cloud_defend/server/routes/policies/policies.ts index 71d7f17ca612e..ad40dcf3b693f 100644 --- a/x-pack/plugins/cloud_defend/server/routes/policies/policies.ts +++ b/x-pack/plugins/cloud_defend/server/routes/policies/policies.ts @@ -42,7 +42,7 @@ const createPolicies = ( const agentPolicyStatus = { id: agentPolicy.id, name: agentPolicy.name, - agents: agentStatusByAgentPolicyId[agentPolicy.id]?.total, + agents: agentStatusByAgentPolicyId[agentPolicy.id]?.active, }; return { package_policy: cloudDefendPackage, 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 90e11734d72c6..86eb6ba963402 100644 --- a/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts +++ b/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts @@ -214,7 +214,7 @@ export const getBenchmarkApplicableTo = (benchmarkId: BenchmarksCisId) => { }; export const getCloudProviderNameFromAbbreviation = (cloudProvider: string) => { - switch (cloudProvider) { + switch (cloudProvider.toLowerCase()) { case 'azure': return CLOUD_PROVIDER_NAMES.AZURE; case 'aws': diff --git a/x-pack/plugins/cloud_security_posture/public/common/constants.ts b/x-pack/plugins/cloud_security_posture/public/common/constants.ts index 50d191cf07167..ea3866cbe1256 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/constants.ts @@ -256,3 +256,29 @@ export const VULNERABILITY_GROUPING_OPTIONS = { CLOUD_ACCOUNT_NAME: VULNERABILITY_FIELDS.CLOUD_ACCOUNT_NAME, CVE: VULNERABILITY_FIELDS.VULNERABILITY_ID, }; + +/* +The fields below are default columns of the Cloud Security Data Table that need to have keyword mapping. +The runtime mappings are used to prevent filtering out the data when any of these columns are sorted in the Data Table. +TODO: Remove the fields below once they are mapped as Keyword in the Third Party integrations, or remove +the fields from the runtime mappings if they are removed from the Data Table. +*/ +export const CDR_VULNERABILITY_DATA_TABLE_RUNTIME_MAPPING_FIELDS: string[] = []; +export const CDR_MISCONFIGURATION_DATA_TABLE_RUNTIME_MAPPING_FIELDS: string[] = [ + 'rule.benchmark.rule_number', + 'rule.section', + 'resource.sub_type', +]; + +/* +The fields below are used to group the data in the Cloud Security Data Table. +The keys are the fields that are used to group the data, and the values are the fields that need to have keyword mapping +to prevent filtering out the data when grouping by the key field. +TODO: Remove the fields below once they are mapped as Keyword in the Third Party integrations, or remove +the fields from the runtime mappings if they are removed from the Data Table. +*/ +export const CDR_VULNERABILITY_GROUPING_RUNTIME_MAPPING_FIELDS: Record = {}; +export const CDR_MISCONFIGURATION_GROUPING_RUNTIME_MAPPING_FIELDS: Record = { + [FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_NAME]: ['orchestrator.cluster.name'], + [FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME]: ['cloud.account.name'], +}; diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_provider_icon.tsx b/x-pack/plugins/cloud_security_posture/public/components/cloud_provider_icon.tsx index b6acdac0ee1b1..a022e38960894 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/cloud_provider_icon.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_provider_icon.tsx @@ -18,7 +18,7 @@ interface Props { } const getCloudProviderIcon = (cloudProvider: string) => { - switch (cloudProvider) { + switch (cloudProvider.toLowerCase()) { case 'azure': return 'logoAzure'; case 'aws': 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 index 6d901a76a29c3..b98ff432a3c96 100644 --- 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 @@ -52,9 +52,6 @@ export interface FindingsGroupingAggregation { resourceSubType?: { buckets?: GenericBuckets[]; }; - resourceType?: { - buckets?: GenericBuckets[]; - }; benchmarkName?: { buckets?: GenericBuckets[]; }; 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 068eb3df1b10f..64353230211bc 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 @@ -22,6 +22,7 @@ import type { CspBenchmarkRulesStates } from '@kbn/cloud-security-posture-common import type { FindingsBaseEsQuery } from '@kbn/cloud-security-posture'; import { useGetCspBenchmarkRulesStatesApi } from '@kbn/cloud-security-posture/src/hooks/use_get_benchmark_rules_state_api'; import type { RuntimePrimitiveTypes } from '@kbn/data-views-plugin/common'; +import { CDR_MISCONFIGURATION_DATA_TABLE_RUNTIME_MAPPING_FIELDS } from '../../../common/constants'; import { useKibana } from '../../../common/hooks/use_kibana'; import { getAggregationCount, getFindingsCountAggQuery } from '../utils/utils'; @@ -41,17 +42,18 @@ interface FindingsAggs { } const getRuntimeMappingsFromSort = (sort: string[][]) => { - return sort.reduce((acc, [field]) => { - // TODO: Add proper type for all fields available in the field selector - const type: RuntimePrimitiveTypes = field === '@timestamp' ? 'date' : 'keyword'; + return sort + .filter(([field]) => CDR_MISCONFIGURATION_DATA_TABLE_RUNTIME_MAPPING_FIELDS.includes(field)) + .reduce((acc, [field]) => { + const type: RuntimePrimitiveTypes = 'keyword'; - return { - ...acc, - [field]: { - type, - }, - }; - }, {}); + return { + ...acc, + [field]: { + type, + }, + }; + }, {}); }; export const getFindingsQuery = ( 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 index e009ee966fb96..45c5418ed5129 100644 --- 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 @@ -21,6 +21,7 @@ import { } from '@kbn/cloud-security-posture-common'; import { useGetCspBenchmarkRulesStatesApi } from '@kbn/cloud-security-posture/src/hooks/use_get_benchmark_rules_state_api'; import { + CDR_MISCONFIGURATION_GROUPING_RUNTIME_MAPPING_FIELDS, FINDINGS_GROUPING_OPTIONS, LOCAL_STORAGE_FINDINGS_GROUPING_KEY, } from '../../../common/constants'; @@ -90,7 +91,6 @@ const getAggregationsByGroupField = (field: string): NamedAggregation[] => { ...aggMetrics, getTermAggregation('resourceName', 'resource.id'), getTermAggregation('resourceSubType', 'resource.sub_type'), - getTermAggregation('resourceType', 'resource.type'), ]; case FINDINGS_GROUPING_OPTIONS.RULE_NAME: return [ @@ -122,62 +122,18 @@ const getAggregationsByGroupField = (field: string): NamedAggregation[] => { const getRuntimeMappingsByGroupField = ( field: string ): Record | undefined => { - switch (field) { - case FINDINGS_GROUPING_OPTIONS.RESOURCE_NAME: - return { - [FINDINGS_GROUPING_OPTIONS.RESOURCE_NAME]: { - type: 'keyword', - }, - 'resource.id': { - type: 'keyword', - }, - 'resource.sub_type': { - type: 'keyword', - }, - 'resource.type': { - type: 'keyword', - }, - }; - case FINDINGS_GROUPING_OPTIONS.RULE_NAME: - return { - [FINDINGS_GROUPING_OPTIONS.RULE_NAME]: { - type: 'keyword', - }, - 'rule.benchmark.version': { - type: 'keyword', - }, - }; - case FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME: - return { - [FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME]: { + if (CDR_MISCONFIGURATION_GROUPING_RUNTIME_MAPPING_FIELDS?.[field]) { + return CDR_MISCONFIGURATION_GROUPING_RUNTIME_MAPPING_FIELDS[field].reduce( + (acc, runtimeField) => ({ + ...acc, + [runtimeField]: { type: 'keyword', }, - 'rule.benchmark.name': { - type: 'keyword', - }, - 'rule.benchmark.id': { - type: 'keyword', - }, - }; - case FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_NAME: - return { - [FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_NAME]: { - type: 'keyword', - }, - 'rule.benchmark.name': { - type: 'keyword', - }, - 'rule.benchmark.id': { - type: 'keyword', - }, - }; - default: - return { - [field]: { - type: 'keyword', - }, - }; + }), + {} + ); } + return {}; }; /** @@ -255,12 +211,7 @@ export const useLatestFindingsGrouping = ({ size: pageSize, sort: [{ groupByField: { order: 'desc' } }, { complianceScore: { order: 'asc' } }], statsAggregations: getAggregationsByGroupField(currentSelectedGroup), - runtimeMappings: { - ...getRuntimeMappingsByGroupField(currentSelectedGroup), - 'result.evaluation': { - type: 'keyword', - }, - }, + runtimeMappings: getRuntimeMappingsByGroupField(currentSelectedGroup), rootAggregations: [ { failedFindings: { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities.tsx index 5f01a4693c8f5..a998707c4704f 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities.tsx @@ -24,7 +24,10 @@ import { import { FindingsBaseEsQuery, showErrorToast } from '@kbn/cloud-security-posture'; import type { CspVulnerabilityFinding } from '@kbn/cloud-security-posture-common/schema/vulnerabilities/latest'; import type { RuntimePrimitiveTypes } from '@kbn/data-views-plugin/common'; -import { VULNERABILITY_FIELDS } from '../../../common/constants'; +import { + CDR_VULNERABILITY_DATA_TABLE_RUNTIME_MAPPING_FIELDS, + VULNERABILITY_FIELDS, +} from '../../../common/constants'; import { useKibana } from '../../../common/hooks/use_kibana'; import { getCaseInsensitiveSortScript } from '../utils/custom_sort_script'; type LatestFindingsRequest = IKibanaSearchRequest; @@ -54,22 +57,18 @@ const getMultiFieldsSort = (sort: string[][]) => { }; const getRuntimeMappingsFromSort = (sort: string[][]) => { - return sort.reduce((acc, [field]) => { - // TODO: Add proper type for all fields available in the field selector - const type: RuntimePrimitiveTypes = - field === VULNERABILITY_FIELDS.SCORE_BASE - ? 'double' - : field === '@timestamp' - ? 'date' - : 'keyword'; + return sort + .filter(([field]) => CDR_VULNERABILITY_DATA_TABLE_RUNTIME_MAPPING_FIELDS.includes(field)) + .reduce((acc, [field]) => { + const type: RuntimePrimitiveTypes = 'keyword'; - return { - ...acc, - [field]: { - type, - }, - }; - }, {}); + return { + ...acc, + [field]: { + type, + }, + }; + }, {}); }; export const getVulnerabilitiesQuery = ( diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_grouping.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_grouping.tsx index d79b4620ec899..1d73b21f083a5 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_grouping.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_grouping.tsx @@ -23,6 +23,7 @@ import { LOCAL_STORAGE_VULNERABILITIES_GROUPING_KEY, VULNERABILITY_GROUPING_OPTIONS, VULNERABILITY_FIELDS, + CDR_VULNERABILITY_GROUPING_RUNTIME_MAPPING_FIELDS, } from '../../../common/constants'; import { useDataViewContext } from '../../../common/contexts/data_view_context'; import { @@ -102,41 +103,18 @@ const getAggregationsByGroupField = (field: string): NamedAggregation[] => { const getRuntimeMappingsByGroupField = ( field: string ): Record | undefined => { - switch (field) { - case VULNERABILITY_GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME: - return { - [VULNERABILITY_GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME]: { - type: 'keyword', - }, - [VULNERABILITY_FIELDS.CLOUD_PROVIDER]: { - type: 'keyword', - }, - }; - case VULNERABILITY_GROUPING_OPTIONS.RESOURCE_NAME: - return { - [VULNERABILITY_GROUPING_OPTIONS.RESOURCE_NAME]: { - type: 'keyword', - }, - [VULNERABILITY_FIELDS.RESOURCE_ID]: { - type: 'keyword', - }, - }; - case VULNERABILITY_GROUPING_OPTIONS.CVE: - return { - [VULNERABILITY_GROUPING_OPTIONS.CVE]: { - type: 'keyword', - }, - [VULNERABILITY_FIELDS.DESCRIPTION]: { - type: 'keyword', - }, - }; - default: - return { - [field]: { + if (CDR_VULNERABILITY_GROUPING_RUNTIME_MAPPING_FIELDS?.[field]) { + return CDR_VULNERABILITY_GROUPING_RUNTIME_MAPPING_FIELDS[field].reduce( + (acc, runtimeField) => ({ + ...acc, + [runtimeField]: { type: 'keyword', }, - }; + }), + {} + ); } + return {}; }; /** diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/v1.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/v1.ts index 611a58436e995..c52b57d057c38 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/v1.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/v1.ts @@ -48,7 +48,7 @@ export const getBenchmarksData = ( const agentPolicyStatus = { id: agentPolicy.id, name: agentPolicy.name, - agents: agentStatusByAgentPolicyId[agentPolicy.id]?.total, + agents: agentStatusByAgentPolicyId[agentPolicy.id]?.active, }; return { package_policy: cspPackage, diff --git a/x-pack/plugins/data_visualizer/public/application/data_drift/data_drift_page.tsx b/x-pack/plugins/data_visualizer/public/application/data_drift/data_drift_page.tsx index 86bb350849a33..4623e886852d8 100644 --- a/x-pack/plugins/data_visualizer/public/application/data_drift/data_drift_page.tsx +++ b/x-pack/plugins/data_visualizer/public/application/data_drift/data_drift_page.tsx @@ -106,31 +106,31 @@ export const PageHeader: FC = ({ onRefresh, needsUpdate }) => { {dataView.getName()}
} + rightSideGroupProps={{ + gutterSize: 's', + 'data-test-subj': 'dataComparisonTimeRangeSelectorSection', + }} rightSideItems={[ - - {hasValidTimeField ? ( - - - - ) : null} - , + hasValidTimeField && ( + - , - ]} + ), + ].filter(Boolean)} /> ); }; diff --git a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.ts b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.tsx similarity index 80% rename from x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.ts rename to x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.tsx index f4631ea96b937..8eefae138b6c3 100644 --- a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.ts +++ b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.tsx @@ -7,19 +7,20 @@ import { BehaviorSubject } from 'rxjs'; import { IExternalUrl } from '@kbn/core/public'; -import { UrlDrilldown, Config } from './url_drilldown'; +import { render, waitFor } from '@testing-library/react'; +import { Config, UrlDrilldown } from './url_drilldown'; import { - ValueClickContext, - VALUE_CLICK_TRIGGER, - SELECT_RANGE_TRIGGER, CONTEXT_MENU_TRIGGER, + SELECT_RANGE_TRIGGER, + VALUE_CLICK_TRIGGER, + ValueClickContext, } from '@kbn/embeddable-plugin/public'; import { DatatableColumnType } from '@kbn/expressions-plugin/common'; -import { of } from '@kbn/kibana-utils-plugin/common'; import { createPoint, rowClickData } from './test/data'; import { ROW_CLICK_TRIGGER } from '@kbn/ui-actions-plugin/public'; import { settingsServiceMock } from '@kbn/core-ui-settings-browser-mocks'; import { themeServiceMock } from '@kbn/core-theme-browser-mocks'; +import React from 'react'; const mockDataPoints = [ { @@ -61,6 +62,7 @@ const mockEmbeddableApi = { filters$: new BehaviorSubject([]), query$: new BehaviorSubject({ query: 'test', language: 'kuery' }), timeRange$: new BehaviorSubject({ from: 'now-15m', to: 'now' }), + viewMode: new BehaviorSubject('edit'), }, }; @@ -93,6 +95,20 @@ const createDrilldown = (isExternalUrlValid: boolean = true) => { return drilldown; }; +const renderActionMenuItem = async ( + drilldown: UrlDrilldown, + config: Config, + context: ValueClickContext +) => { + const { getByTestId } = render( + + ); + await waitFor(() => null); // wait for effects to complete + return { + getError: () => getByTestId('urlDrilldown-error'), + }; +}; + describe('UrlDrilldown', () => { const urlDrilldown = createDrilldown(); @@ -119,7 +135,73 @@ describe('UrlDrilldown', () => { await expect(urlDrilldown.isCompatible(config, context)).rejects.toThrowError(); }); - test('compatible if url is valid', async () => { + test('compatible in edit mode if url is valid', async () => { + const config: Config = { + url: { + template: `https://elasti.co/?{{event.value}}&{{rison context.panel.query}}`, + }, + openInNewTab: false, + encodeUrl: true, + }; + + const context: ValueClickContext = { + data: { + data: mockDataPoints, + }, + embeddable: mockEmbeddableApi, + }; + + const result = urlDrilldown.isCompatible(config, context); + await expect(result).resolves.toBe(true); + }); + + test('compatible in edit mode if url is invalid', async () => { + const config: Config = { + url: { + template: `https://elasti.co/?{{event.value}}&{{rison context.panel.somethingFake}}`, + }, + openInNewTab: false, + encodeUrl: true, + }; + + const context: ValueClickContext = { + data: { + data: mockDataPoints, + }, + embeddable: mockEmbeddableApi, + }; + + await expect(urlDrilldown.isCompatible(config, context)).resolves.toBe(true); + }); + + test('compatible in edit mode if external URL is denied', async () => { + const drilldown1 = createDrilldown(true); + const drilldown2 = createDrilldown(false); + const config: Config = { + url: { + template: `https://elasti.co/?{{event.value}}&{{rison context.panel.query}}`, + }, + openInNewTab: false, + encodeUrl: true, + }; + + const context: ValueClickContext = { + data: { + data: mockDataPoints, + }, + embeddable: mockEmbeddableApi, + }; + + const result1 = await drilldown1.isCompatible(config, context); + const result2 = await drilldown2.isCompatible(config, context); + + expect(result1).toBe(true); + expect(result2).toBe(true); + }); + + test('compatible in view mode if url is valid', async () => { + mockEmbeddableApi.parentApi.viewMode.next('view'); + const config: Config = { url: { template: `https://elasti.co/?{{event.value}}&{{rison context.panel.query}}`, @@ -139,7 +221,8 @@ describe('UrlDrilldown', () => { await expect(result).resolves.toBe(true); }); - test('not compatible if url is invalid', async () => { + test('not compatible in view mode if url is invalid', async () => { + mockEmbeddableApi.parentApi.viewMode.next('view'); const config: Config = { url: { template: `https://elasti.co/?{{event.value}}&{{rison context.panel.somethingFake}}`, @@ -158,7 +241,8 @@ describe('UrlDrilldown', () => { await expect(urlDrilldown.isCompatible(config, context)).resolves.toBe(false); }); - test('not compatible if external URL is denied', async () => { + test('not compatible in view mode if external URL is denied', async () => { + mockEmbeddableApi.parentApi.viewMode.next('view'); const drilldown1 = createDrilldown(true); const drilldown2 = createDrilldown(false); const config: Config = { @@ -184,7 +268,7 @@ describe('UrlDrilldown', () => { }); }); - describe('getHref & execute', () => { + describe('getHref & execute & title', () => { beforeEach(() => { mockNavigateToUrl.mockReset(); }); @@ -210,6 +294,9 @@ describe('UrlDrilldown', () => { await urlDrilldown.execute(config, context); expect(mockNavigateToUrl).toBeCalledWith(url); + + const { getError } = await renderActionMenuItem(urlDrilldown, config, context); + expect(() => getError()).toThrow(); }); test('invalid url', async () => { @@ -228,12 +315,17 @@ describe('UrlDrilldown', () => { embeddable: mockEmbeddableApi, }; - await expect(urlDrilldown.getHref(config, context)).rejects.toThrowError(); - await expect(urlDrilldown.execute(config, context)).rejects.toThrowError(); + await expect(urlDrilldown.getHref(config, context)).resolves.toBeUndefined(); + await expect(urlDrilldown.execute(config, context)).resolves.toBeUndefined(); expect(mockNavigateToUrl).not.toBeCalled(); + + const { getError } = await renderActionMenuItem(urlDrilldown, config, context); + expect(getError()).toHaveTextContent( + `Error building URL: The URL template is not valid in the given context.` + ); }); - test('should throw on denied external URL', async () => { + test('should not throw on denied external URL', async () => { const drilldown1 = createDrilldown(true); const drilldown2 = createDrilldown(false); const config: Config = { @@ -257,17 +349,11 @@ describe('UrlDrilldown', () => { expect(url).toMatchInlineSnapshot(`"https://elasti.co/?test&(language:kuery,query:test)"`); expect(mockNavigateToUrl).toBeCalledWith(url); - const [, error1] = await of(drilldown2.getHref(config, context)); - const [, error2] = await of(drilldown2.execute(config, context)); + await expect(drilldown2.getHref(config, context)).resolves.toBeUndefined(); + await expect(drilldown2.execute(config, context)).resolves.toBeUndefined(); - expect(error1).toBeInstanceOf(Error); - expect(error1.message).toMatchInlineSnapshot( - `"External URL [https://elasti.co/?test&(language:kuery,query:test)] was denied by ExternalUrl service. You can configure external URL policies using \\"externalUrl.policy\\" setting in kibana.yml."` - ); - expect(error2).toBeInstanceOf(Error); - expect(error2.message).toMatchInlineSnapshot( - `"External URL [https://elasti.co/?test&(language:kuery,query:test)] was denied by ExternalUrl service. You can configure external URL policies using \\"externalUrl.policy\\" setting in kibana.yml."` - ); + const { getError } = await renderActionMenuItem(drilldown2, config, context); + expect(getError()).toHaveTextContent(`Error building URL: external URL was denied.`); }); }); diff --git a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.tsx b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.tsx index 1dd9c94ef329f..fed0542883611 100644 --- a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.tsx +++ b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.tsx @@ -7,7 +7,11 @@ import React from 'react'; import { IExternalUrl, ThemeServiceStart } from '@kbn/core/public'; -import type { EmbeddableApiContext } from '@kbn/presentation-publishing'; +import { + type EmbeddableApiContext, + getInheritedViewMode, + apiCanAccessViewMode, +} from '@kbn/presentation-publishing'; import { ChartActionContext, CONTEXT_MENU_TRIGGER, @@ -17,21 +21,23 @@ import { import { IMAGE_CLICK_TRIGGER } from '@kbn/image-embeddable-plugin/public'; import { ActionExecutionContext, ROW_CLICK_TRIGGER } from '@kbn/ui-actions-plugin/public'; import type { CollectConfigProps as CollectConfigPropsBase } from '@kbn/kibana-utils-plugin/public'; -import { UrlTemplateEditorVariable, KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaContextProvider, UrlTemplateEditorVariable } from '@kbn/kibana-react-plugin/public'; import { + UiActionsEnhancedBaseActionFactoryContext as BaseActionFactoryContext, UiActionsEnhancedDrilldownDefinition as Drilldown, - UrlDrilldownGlobalScope, - UrlDrilldownConfig, UrlDrilldownCollectConfig, - urlDrilldownValidateUrlTemplate, urlDrilldownCompileUrl, - UiActionsEnhancedBaseActionFactoryContext as BaseActionFactoryContext, + UrlDrilldownConfig, + UrlDrilldownGlobalScope, + urlDrilldownValidateUrlTemplate, } from '@kbn/ui-actions-enhanced-plugin/public'; import type { SerializedAction } from '@kbn/ui-actions-enhanced-plugin/common/types'; import type { SettingsStart } from '@kbn/core-ui-settings-browser'; +import { EuiText, EuiTextBlockTruncate } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { txtUrlDrilldownDisplayName } from './i18n'; -import { getEventVariableList, getEventScopeValues } from './variables/event_variables'; -import { getContextVariableList, getContextScopeValues } from './variables/context_variables'; +import { getEventScopeValues, getEventVariableList } from './variables/event_variables'; +import { getContextScopeValues, getContextVariableList } from './variables/context_variables'; import { getGlobalVariableList } from './variables/global_variables'; interface UrlDrilldownDeps { @@ -58,6 +64,13 @@ export type CollectConfigProps = CollectConfigPropsBase { + if (apiCanAccessViewMode(context.embeddable)) { + return getInheritedViewMode(context.embeddable); + } + throw new Error('Cannot access view mode'); +}; + export class UrlDrilldown implements Drilldown { public readonly id = URL_DRILLDOWN; @@ -75,20 +88,39 @@ export class UrlDrilldown implements Drilldown; }> = ({ config, context }) => { const [title, setTitle] = React.useState(config.name); + const [error, setError] = React.useState(); React.useEffect(() => { - let unmounted = false; const variables = this.getRuntimeVariables(context); urlDrilldownCompileUrl(title, variables, false) .then((result) => { - if (unmounted) return; if (title !== result) setTitle(result); }) .catch(() => {}); - return () => { - unmounted = true; - }; - }); - return <>{title}; + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + React.useEffect(() => { + this.buildUrl(config.config, context).catch((e) => { + setError(e.message); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( + /* title is used as a tooltip, EuiToolTip doesn't work in this context menu due to hacky zIndex */ + + {title} + {/* note: ideally we'd use EuiIconTip for the error, but it doesn't play well with this context menu*/} + {error ? ( + + + {error} + + + ) : null} + + ); }; public readonly euiIcon = 'link'; @@ -140,53 +172,81 @@ export class UrlDrilldown implements Drilldown { - const scope = this.getRuntimeVariables(context); - const { isValid, error } = await urlDrilldownValidateUrlTemplate(config.url, scope); + const viewMode = getViewMode(context); - if (!isValid) { - // eslint-disable-next-line no-console - console.warn( - `UrlDrilldown [${config.url.template}] is not valid. Error [${error}]. Skipping execution.` - ); - return false; + if (viewMode === 'edit') { + // check if context is compatible by building the scope + const scope = this.getRuntimeVariables(context); + return !!scope; } - const url = await this.buildUrl(config, context); - const validUrl = this.deps.externalUrl.validateUrl(url); - if (!validUrl) { + try { + await this.buildUrl(config, context); + return true; + } catch (e) { + // eslint-disable-next-line no-console + console.warn(e); return false; } - - return true; }; private async buildUrl(config: Config, context: ChartActionContext): Promise { + const scope = this.getRuntimeVariables(context); + const { isValid, error, invalidUrl } = await urlDrilldownValidateUrlTemplate(config.url, scope); + + if (!isValid) { + const errorMessage = i18n.translate('xpack.urlDrilldown.invalidUrlErrorMessage', { + defaultMessage: + 'Error building URL: {error} Use drilldown editor to check your URL template. Invalid URL: {invalidUrl}', + values: { + error, + invalidUrl, + }, + }); + throw new Error(errorMessage); + } + const doEncode = config.encodeUrl ?? true; + const url = await urlDrilldownCompileUrl( config.url.template, this.getRuntimeVariables(context), doEncode ); + + const validUrl = this.deps.externalUrl.validateUrl(url); + if (!validUrl) { + const errorMessage = i18n.translate('xpack.urlDrilldown.invalidUrlErrorMessage', { + defaultMessage: + 'Error building URL: external URL was denied. Administrator can configure external URL policies using "externalUrl.policy" setting in kibana.yml. Invalid URL: {invalidUrl}', + values: { + invalidUrl: url, + }, + }); + throw new Error(errorMessage); + } + return url; } public readonly getHref = async ( config: Config, context: ChartActionContext - ): Promise => { - const url = await this.buildUrl(config, context); - const validUrl = this.deps.externalUrl.validateUrl(url); - if (!validUrl) { - throw new Error( - `External URL [${url}] was denied by ExternalUrl service. ` + - `You can configure external URL policies using "externalUrl.policy" setting in kibana.yml.` - ); + ): Promise => { + try { + const url = await this.buildUrl(config, context); + return url; + } catch (e) { + // eslint-disable-next-line no-console + console.warn(e); + return undefined; } - return url; }; public readonly execute = async (config: Config, context: ChartActionContext) => { const url = await this.getHref(config, context); + if (!url) return; + if (config.openInNewTab) { window.open(url, '_blank', 'noopener'); } else { diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/lib/fetch_available_indices.test.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/lib/fetch_available_indices.test.ts new file mode 100644 index 0000000000000..fa26fb68289a6 --- /dev/null +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/lib/fetch_available_indices.test.ts @@ -0,0 +1,454 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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/server'; +import moment from 'moment-timezone'; + +import type { + FetchAvailableCatIndicesResponseRequired, + IndexSearchAggregationResponse, +} from './fetch_available_indices'; +import { fetchAvailableIndices } from './fetch_available_indices'; + +function getEsClientMock() { + return { + search: jest.fn().mockResolvedValue({ + aggregations: { + index: { + buckets: [], + }, + }, + }), + cat: { + indices: jest.fn().mockResolvedValue([]), + }, + } as unknown as ElasticsearchClient & { + cat: { + indices: jest.Mock>; + }; + search: jest.Mock>; + }; +} + +// fixing timezone for both Date and moment +// so when tests are run in different timezones, the results are consistent +process.env.TZ = 'UTC'; +moment.tz.setDefault('UTC'); + +const DAY_IN_MILLIS = 24 * 60 * 60 * 1000; + +// We assume that the dates are in UTC, because es is using UTC +// It also diminishes difference date parsing by Date and moment constructors +// in different timezones, i.e. short ISO format '2021-10-01' is parsed as local +// date by moment and as UTC date by Date, whereas long ISO format '2021-10-01T00:00:00Z' +// is parsed as UTC date by both +const startDateString: string = '2021-10-01T00:00:00Z'; +const endDateString: string = '2021-10-07T00:00:00Z'; + +const startDateMillis: number = new Date(startDateString).getTime(); +const endDateMillis: number = new Date(endDateString).getTime(); + +describe('fetchAvailableIndices', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('aggregate search given index by startDate and endDate', async () => { + const esClientMock = getEsClientMock(); + + await fetchAvailableIndices(esClientMock, { + indexPattern: 'logs-*', + startDate: startDateString, + endDate: endDateString, + }); + + expect(esClientMock.search).toHaveBeenCalledWith({ + query: { + bool: { + filter: [ + { + range: { + '@timestamp': { + format: 'strict_date_optional_time', + gte: startDateString, + lte: endDateString, + }, + }, + }, + ], + must: [], + must_not: [], + should: [], + }, + }, + index: 'logs-*', + size: 0, + aggs: { + index: { + terms: { + field: '_index', + }, + }, + }, + }); + }); + + it('should call esClient.cat.indices for given index', async () => { + const esClientMock = getEsClientMock(); + + await fetchAvailableIndices(esClientMock, { + indexPattern: 'logs-*', + startDate: startDateString, + endDate: endDateString, + }); + + expect(esClientMock.cat.indices).toHaveBeenCalledWith({ + index: 'logs-*', + format: 'json', + h: 'index,creation.date', + }); + }); + + describe('when indices are created within the date range', () => { + it('returns indices within the date range', async () => { + const esClientMock = getEsClientMock(); + + esClientMock.cat.indices.mockResolvedValue([ + { + index: 'logs-2021.10.01', + 'creation.date': `${startDateMillis}`, + }, + { + index: 'logs-2021.10.05', + 'creation.date': `${startDateMillis + 4 * DAY_IN_MILLIS}`, + }, + { + index: 'logs-2021.09.30', + 'creation.date': `${startDateMillis - DAY_IN_MILLIS}`, + }, + ]); + + const result = await fetchAvailableIndices(esClientMock, { + indexPattern: 'logs-*', + startDate: startDateString, + endDate: endDateString, + }); + + expect(result).toEqual(['logs-2021.10.01', 'logs-2021.10.05']); + + expect(esClientMock.cat.indices).toHaveBeenCalledWith({ + index: 'logs-*', + format: 'json', + h: 'index,creation.date', + }); + }); + }); + + describe('when indices are outside the date range', () => { + it('returns an empty list', async () => { + const esClientMock = getEsClientMock(); + + esClientMock.cat.indices.mockResolvedValue([ + { + index: 'logs-2021.09.30', + 'creation.date': `${startDateMillis - DAY_IN_MILLIS}`, + }, + { + index: 'logs-2021.10.08', + 'creation.date': `${endDateMillis + DAY_IN_MILLIS}`, + }, + ]); + + const result = await fetchAvailableIndices(esClientMock, { + indexPattern: 'logs-*', + startDate: startDateString, + endDate: endDateString, + }); + + expect(result).toEqual([]); + }); + }); + + describe('when no indices match the index pattern', () => { + it('returns empty list', async () => { + const esClientMock = getEsClientMock(); + + esClientMock.cat.indices.mockResolvedValue([]); + + const result = await fetchAvailableIndices(esClientMock, { + indexPattern: 'nonexistent-*', + startDate: startDateString, + endDate: endDateString, + }); + + expect(result).toEqual([]); + }); + }); + + describe('when indices have data in the date range', () => { + it('returns indices with data in the date range', async () => { + const esClientMock = getEsClientMock(); + + // esClient.cat.indices returns no indices + esClientMock.cat.indices.mockResolvedValue([]); + + // esClient.search returns indices with data in the date range + esClientMock.search.mockResolvedValue({ + aggregations: { + index: { + buckets: [ + { key: 'logs-2021.10.02', doc_count: 100 }, + { key: 'logs-2021.10.03', doc_count: 150 }, + ], + }, + }, + }); + + const result = await fetchAvailableIndices(esClientMock, { + indexPattern: 'logs-*', + startDate: startDateString, + endDate: endDateString, + }); + + expect(result).toEqual(['logs-2021.10.02', 'logs-2021.10.03']); + }); + + it('combines indices from both methods without duplicates', async () => { + const esClientMock = getEsClientMock(); + + esClientMock.cat.indices.mockResolvedValue([ + { + index: 'logs-2021.10.01', + 'creation.date': `${startDateMillis}`, + }, + { + index: 'logs-2021.10.03', + 'creation.date': `${startDateMillis + 2 * DAY_IN_MILLIS}`, + }, + ]); + + esClientMock.search.mockResolvedValue({ + aggregations: { + index: { + buckets: [ + { key: 'logs-2021.10.03', doc_count: 150 }, + { key: 'logs-2021.10.04', doc_count: 200 }, + ], + }, + }, + }); + + const result = await fetchAvailableIndices(esClientMock, { + indexPattern: 'logs-*', + startDate: startDateString, + endDate: endDateString, + }); + + expect(result).toEqual(['logs-2021.10.01', 'logs-2021.10.03', 'logs-2021.10.04']); + }); + }); + + describe('edge cases for creation dates', () => { + it('includes indices with creation date exactly at startDate and endDate', async () => { + const esClientMock = getEsClientMock(); + + esClientMock.cat.indices.mockResolvedValue([ + { + index: 'logs-2021.10.01', + 'creation.date': `${startDateMillis}`, + }, + { + index: 'logs-2021.10.07', + 'creation.date': `${endDateMillis}`, + }, + ]); + + const result = await fetchAvailableIndices(esClientMock, { + indexPattern: 'logs-*', + startDate: startDateString, + endDate: endDateString, + }); + + expect(result).toEqual(['logs-2021.10.01', 'logs-2021.10.07']); + }); + }); + + describe('when esClient.search rejects', () => { + it('throws an error', async () => { + const esClientMock = getEsClientMock(); + + esClientMock.search.mockRejectedValue(new Error('Elasticsearch search error')); + + await expect( + fetchAvailableIndices(esClientMock, { + indexPattern: 'logs-*', + startDate: startDateString, + endDate: endDateString, + }) + ).rejects.toThrow('Elasticsearch search error'); + }); + }); + + describe('when both esClient.cat.indices and esClient.search return empty', () => { + it('returns an empty list', async () => { + const esClientMock = getEsClientMock(); + + esClientMock.cat.indices.mockResolvedValue([]); + esClientMock.search.mockResolvedValue({ + aggregations: { + index: { + buckets: [], + }, + }, + }); + + const result = await fetchAvailableIndices(esClientMock, { + indexPattern: 'logs-*', + startDate: startDateString, + endDate: endDateString, + }); + + expect(result).toEqual([]); + }); + }); + + describe('when indices are returned with both methods and have duplicates', () => { + it('does not duplicate indices in the result', async () => { + const esClientMock = getEsClientMock(); + + esClientMock.cat.indices.mockResolvedValue([ + { + index: 'logs-2021.10.05', + 'creation.date': `${startDateMillis + 4 * DAY_IN_MILLIS}`, + }, + ]); + + esClientMock.search.mockResolvedValue({ + aggregations: { + index: { + buckets: [{ key: 'logs-2021.10.05', doc_count: 100 }], + }, + }, + }); + + const result = await fetchAvailableIndices(esClientMock, { + indexPattern: 'logs-*', + startDate: startDateString, + endDate: endDateString, + }); + + expect(result).toEqual(['logs-2021.10.05']); + }); + }); + + describe('given keyword dates', () => { + describe('given 7 days range', () => { + beforeEach(() => { + jest.useFakeTimers(); + jest.setSystemTime(new Date('2021-10-07T00:00:00Z').getTime()); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('finds indices created within the date range', async () => { + const esClientMock = getEsClientMock(); + + esClientMock.cat.indices.mockResolvedValue([ + { + index: 'logs-2021.10.01', + 'creation.date': `${startDateMillis}`, + }, + { + index: 'logs-2021.10.05', + 'creation.date': `${startDateMillis + 4 * DAY_IN_MILLIS}`, + }, + ]); + + const results = await fetchAvailableIndices(esClientMock, { + indexPattern: 'logs-*', + startDate: 'now-7d/d', + endDate: 'now/d', + }); + + expect(results).toEqual(['logs-2021.10.01', 'logs-2021.10.05']); + }); + + it('finds indices with end date rounded up to the end of the day', async () => { + const esClientMock = getEsClientMock(); + + esClientMock.cat.indices.mockResolvedValue([ + { + index: 'logs-2021.10.06', + 'creation.date': `${new Date('2021-10-06T23:59:59Z').getTime()}`, + }, + ]); + + const results = await fetchAvailableIndices(esClientMock, { + indexPattern: 'logs-*', + startDate: 'now-7d/d', + endDate: 'now-1d/d', + }); + + expect(results).toEqual(['logs-2021.10.06']); + }); + }); + }); + + describe('rejections', () => { + beforeEach(() => { + jest.spyOn(console, 'warn').mockImplementation(() => {}); + }); + afterEach(() => { + jest.restoreAllMocks(); + }); + describe('when esClient.cat.indices rejects', () => { + it('throws an error', async () => { + const esClientMock = getEsClientMock(); + + esClientMock.cat.indices.mockRejectedValue(new Error('Elasticsearch error')); + + await expect( + fetchAvailableIndices(esClientMock, { + indexPattern: 'logs-*', + startDate: startDateString, + endDate: endDateString, + }) + ).rejects.toThrow('Elasticsearch error'); + }); + }); + + describe('when startDate is invalid', () => { + it('throws an error', async () => { + const esClientMock = getEsClientMock(); + + await expect( + fetchAvailableIndices(esClientMock, { + indexPattern: 'logs-*', + startDate: 'invalid-date', + endDate: endDateString, + }) + ).rejects.toThrow('Invalid date format: invalid-date'); + }); + }); + + describe('when endDate is invalid', () => { + it('throws an error', async () => { + const esClientMock = getEsClientMock(); + + await expect( + fetchAvailableIndices(esClientMock, { + indexPattern: 'logs-*', + startDate: startDateString, + endDate: 'invalid-date', + }) + ).rejects.toThrow('Invalid date format: invalid-date'); + }); + }); + }); +}); diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/lib/fetch_available_indices.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/lib/fetch_available_indices.ts index 584a261689113..32311f28d636a 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/lib/fetch_available_indices.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/lib/fetch_available_indices.ts @@ -4,18 +4,72 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import type { ElasticsearchClient } from '@kbn/core/server'; +import type { CatIndicesIndicesRecord } from '@elastic/elasticsearch/lib/api/types'; +import dateMath from '@kbn/datemath'; + import { getRequestBody } from '../helpers/get_available_indices'; +export type FetchAvailableCatIndicesResponseRequired = Array< + Required> +>; + type AggregateName = 'index'; -interface Result { +export interface IndexSearchAggregationResponse { index: { - buckets: Array<{ key: string }>; - doc_count: number; + buckets: Array<{ key: string; doc_count: number }>; }; } -export const fetchAvailableIndices = ( +const getParsedDateMs = (dateStr: string, roundUp = false) => { + const date = dateMath.parse(dateStr, roundUp ? { roundUp: true } : undefined); + if (!date?.isValid()) { + throw new Error(`Invalid date format: ${dateStr}`); + } + return date.valueOf(); +}; + +export const fetchAvailableIndices = async ( esClient: ElasticsearchClient, params: { indexPattern: string; startDate: string; endDate: string } -) => esClient.search(getRequestBody(params)); +): Promise => { + const { indexPattern, startDate, endDate } = params; + + const startDateMs = getParsedDateMs(startDate); + const endDateMs = getParsedDateMs(endDate, true); + + const indicesCats = (await esClient.cat.indices({ + index: indexPattern, + format: 'json', + h: 'index,creation.date', + })) as FetchAvailableCatIndicesResponseRequired; + + const indicesCatsInRange = indicesCats.filter((indexInfo) => { + const creationDateMs = parseInt(indexInfo['creation.date'], 10); + return creationDateMs >= startDateMs && creationDateMs <= endDateMs; + }); + + const timeSeriesIndicesWithDataInRangeSearchResult = await esClient.search< + AggregateName, + IndexSearchAggregationResponse + >(getRequestBody(params)); + + const timeSeriesIndicesWithDataInRange = + timeSeriesIndicesWithDataInRangeSearchResult.aggregations?.index.buckets.map( + (bucket) => bucket.key + ) || []; + + // Combine indices from both sources removing duplicates + const resultingIndices = new Set(); + + for (const indicesCat of indicesCatsInRange) { + resultingIndices.add(indicesCat.index); + } + + for (const timeSeriesIndex of timeSeriesIndicesWithDataInRange) { + resultingIndices.add(timeSeriesIndex); + } + + return Array.from(resultingIndices); +}; diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/lib/fetch_stats.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/lib/fetch_stats.ts index 536fd461c61c9..40fc59219342c 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/lib/fetch_stats.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/lib/fetch_stats.ts @@ -57,16 +57,16 @@ export const parseMeteringStats = (meteringStatsIndices: MeteringStatsIndex[]) = }, {}); export const pickAvailableMeteringStats = ( - indicesBuckets: Array<{ key: string }>, + indicesBuckets: string[], meteringStatsIndices: Record ) => - indicesBuckets.reduce((acc: Record, { key }: { key: string }) => { - if (meteringStatsIndices?.[key]) { - acc[key] = { - name: meteringStatsIndices?.[key].name, - num_docs: meteringStatsIndices?.[key].num_docs, + indicesBuckets.reduce((acc: Record, indexName: string) => { + if (meteringStatsIndices?.[indexName]) { + acc[indexName] = { + name: meteringStatsIndices?.[indexName].name, + num_docs: meteringStatsIndices?.[indexName].num_docs, size_in_bytes: null, // We don't have size_in_bytes intentionally when ILM is not available - data_stream: meteringStatsIndices?.[key].data_stream, + data_stream: meteringStatsIndices?.[indexName].data_stream, }; } return acc; diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_ilm_explain.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_ilm_explain.ts index 31202adffed2c..ee413ea09ef67 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_ilm_explain.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_ilm_explain.ts @@ -19,7 +19,11 @@ export const getILMExplainRoute = (router: IRouter, logger: Logger) => { .get({ path: GET_ILM_EXPLAIN, access: 'internal', - options: { tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, }) .addVersion( { diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_index_mappings.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_index_mappings.ts index f3c59ccf9f3e2..845e118bc6f05 100755 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_index_mappings.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_index_mappings.ts @@ -19,7 +19,11 @@ export const getIndexMappingsRoute = (router: IRouter, logger: Logger) => { .get({ path: GET_INDEX_MAPPINGS, access: 'internal', - options: { tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, }) .addVersion( { diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_index_stats.test.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_index_stats.test.ts index f3ff5ec256ad6..91996a4ab9f89 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_index_stats.test.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_index_stats.test.ts @@ -152,17 +152,7 @@ describe('getIndexStatsRoute route', () => { }, }; (fetchMeteringStats as jest.Mock).mockResolvedValue(mockMeteringStatsIndex); - (fetchAvailableIndices as jest.Mock).mockResolvedValue({ - aggregations: { - index: { - buckets: [ - { - key: 'my-index-000001', - }, - ], - }, - }, - }); + (fetchAvailableIndices as jest.Mock).mockResolvedValue(['my-index-000001']); const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); @@ -198,7 +188,7 @@ describe('getIndexStatsRoute route', () => { ); }); - test('returns an empty object when "availableIndices" indices are not available', async () => { + test('returns an empty object when "availableIndices" indices are empty', async () => { const request = requestMock.create({ method: 'get', path: GET_INDEX_STATS, @@ -214,9 +204,7 @@ describe('getIndexStatsRoute route', () => { const mockIndices = {}; (fetchMeteringStats as jest.Mock).mockResolvedValue(mockMeteringStatsIndex); - (fetchAvailableIndices as jest.Mock).mockResolvedValue({ - aggregations: undefined, - }); + (fetchAvailableIndices as jest.Mock).mockResolvedValue([]); const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_index_stats.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_index_stats.ts index 665c178c62cdf..d1bb25d34fc2a 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_index_stats.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_index_stats.ts @@ -26,7 +26,11 @@ export const getIndexStatsRoute = (router: IRouter, logger: Logger) => { .get({ path: GET_INDEX_STATS, access: 'internal', - options: { tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, }) .addVersion( { @@ -86,7 +90,7 @@ export const getIndexStatsRoute = (router: IRouter, logger: Logger) => { endDate: decodedEndDate, }); - if (!availableIndices.aggregations?.index?.buckets) { + if (availableIndices.length === 0) { logger.warn( `No available indices found under pattern: ${decodedIndexName}, in the given date range: ${decodedStartDate} - ${decodedEndDate}` ); @@ -95,10 +99,7 @@ export const getIndexStatsRoute = (router: IRouter, logger: Logger) => { }); } - const indices = pickAvailableMeteringStats( - availableIndices.aggregations.index.buckets, - meteringStatsIndices - ); + const indices = pickAvailableMeteringStats(availableIndices, meteringStatsIndices); return response.ok({ body: indices, diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_unallowed_field_values.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_unallowed_field_values.ts index 76f0827caaad2..9fb743d207d08 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_unallowed_field_values.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_unallowed_field_values.ts @@ -19,6 +19,11 @@ export const getUnallowedFieldValuesRoute = (router: IRouter, logger: Logger) => .post({ path: GET_UNALLOWED_FIELD_VALUES, access: 'internal', + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, }) .addVersion( { diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_index_results.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_index_results.ts index bbab7dede3c21..71f2422146f9c 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_index_results.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_index_results.ts @@ -87,7 +87,11 @@ export const getIndexResultsRoute = ( .get({ path: GET_INDEX_RESULTS, access: 'internal', - options: { tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, }) .addVersion( { diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_index_results_latest.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_index_results_latest.ts index 55ff8e01a01dc..3a294409af869 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_index_results_latest.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_index_results_latest.ts @@ -41,7 +41,11 @@ export const getIndexResultsLatestRoute = ( .get({ path: GET_INDEX_RESULTS_LATEST, access: 'internal', - options: { tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, }) .addVersion( { diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/post_index_results.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/post_index_results.ts index 5e87cadb4dadf..0c627d4cc0364 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/post_index_results.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/post_index_results.ts @@ -24,7 +24,11 @@ export const postIndexResultsRoute = ( .post({ path: POST_INDEX_RESULTS, access: 'internal', - options: { tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, }) .addVersion( { diff --git a/x-pack/plugins/ecs_data_quality_dashboard/tsconfig.json b/x-pack/plugins/ecs_data_quality_dashboard/tsconfig.json index ceb43169165b4..cf31d7461b509 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/tsconfig.json +++ b/x-pack/plugins/ecs_data_quality_dashboard/tsconfig.json @@ -26,6 +26,7 @@ "@kbn/core-elasticsearch-server-mocks", "@kbn/core-elasticsearch-server", "@kbn/core-security-common", + "@kbn/datemath", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/elastic_assistant/server/__mocks__/msearch_query.ts b/x-pack/plugins/elastic_assistant/server/__mocks__/msearch_query.ts index e411dfaa2f1ef..ae5adcfab61aa 100644 --- a/x-pack/plugins/elastic_assistant/server/__mocks__/msearch_query.ts +++ b/x-pack/plugins/elastic_assistant/server/__mocks__/msearch_query.ts @@ -34,12 +34,10 @@ export const mSearchQueryBody: MsearchQueryBody = { ], must: [ { - text_expansion: { - 'vector.tokens': { - model_id: '.elser_model_2', - model_text: - 'Generate an ESQL query that will count the number of connections made to external IP addresses, broken down by user. If the count is greater than 100 for a specific user, add a new field called "follow_up" that contains a value of "true", otherwise, it should contain "false". The user names should also be enriched with their respective group names.', - }, + semantic: { + field: 'semantic_text', + query: + 'Generate an ESQL query that will count the number of connections made to external IP addresses, broken down by user. If the count is greater than 100 for a specific user, add a new field called "follow_up" that contains a value of "true", otherwise, it should contain "false". The user names should also be enriched with their respective group names.', }, }, ], diff --git a/x-pack/plugins/elastic_assistant/server/__mocks__/request.ts b/x-pack/plugins/elastic_assistant/server/__mocks__/request.ts index 9dc57bab25ef3..f6f3007c8f948 100644 --- a/x-pack/plugins/elastic_assistant/server/__mocks__/request.ts +++ b/x-pack/plugins/elastic_assistant/server/__mocks__/request.ts @@ -23,6 +23,7 @@ import { ELASTIC_AI_ASSISTANT_CONVERSATIONS_URL_BY_ID_MESSAGES, ELASTIC_AI_ASSISTANT_CONVERSATIONS_URL_FIND, ELASTIC_AI_ASSISTANT_EVALUATE_URL, + ELASTIC_AI_ASSISTANT_KNOWLEDGE_BASE_INDICES_URL, ELASTIC_AI_ASSISTANT_KNOWLEDGE_BASE_URL, ELASTIC_AI_ASSISTANT_PROMPTS_URL_BULK_ACTION, ELASTIC_AI_ASSISTANT_PROMPTS_URL_FIND, @@ -46,6 +47,12 @@ export const requestMock = { create: httpServerMock.createKibanaRequest, }; +export const getGetKnowledgeBaseIndicesRequest = () => + requestMock.create({ + method: 'get', + path: ELASTIC_AI_ASSISTANT_KNOWLEDGE_BASE_INDICES_URL, + }); + export const getGetKnowledgeBaseStatusRequest = (resource?: string) => requestMock.create({ method: 'get', diff --git a/x-pack/plugins/elastic_assistant/server/__mocks__/vector_search_query.ts b/x-pack/plugins/elastic_assistant/server/__mocks__/vector_search_query.ts index 30fbd0ad2c58f..04263c5d242bb 100644 --- a/x-pack/plugins/elastic_assistant/server/__mocks__/vector_search_query.ts +++ b/x-pack/plugins/elastic_assistant/server/__mocks__/vector_search_query.ts @@ -26,12 +26,10 @@ export const mockVectorSearchQuery: QueryDslQueryContainer = { ], must: [ { - text_expansion: { - 'vector.tokens': { - model_id: '.elser_model_2', - model_text: - 'Generate an ES|QL query that will count the number of connections made to external IP addresses, broken down by user. If the count is greater than 100 for a specific user, add a new field called "follow_up" that contains a value of "true", otherwise, it should contain "false". The user names should also be enriched with their respective group names.', - }, + semantic: { + field: 'semantic_text', + query: + 'Generate an ES|QL query that will count the number of connections made to external IP addresses, broken down by user. If the count is greater than 100 for a specific user, add a new field called "follow_up" that contains a value of "true", otherwise, it should contain "false". The user names should also be enriched with their respective group names.', }, }, ], diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/anonymization_fields/helpers.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/anonymization_fields/helpers.ts index 9a4a3b6e1c0ce..0f577df4e56e1 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/anonymization_fields/helpers.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/anonymization_fields/helpers.ts @@ -99,7 +99,8 @@ export const getUpdateScript = ({ isPatch?: boolean; }) => { return { - source: ` + script: { + source: ` if (params.assignEmpty == true || params.containsKey('allowed')) { ctx._source.allowed = params.allowed; } @@ -108,11 +109,12 @@ export const getUpdateScript = ({ } ctx._source.updated_at = params.updated_at; `, - lang: 'painless', - params: { - ...anonymizationField, // when assigning undefined in painless, it will remove property and wil set it to null - // for patch we don't want to remove unspecified value in payload - assignEmpty: !(isPatch ?? true), + lang: 'painless', + params: { + ...anonymizationField, // when assigning undefined in painless, it will remove property and wil set it to null + // for patch we don't want to remove unspecified value in payload + assignEmpty: !(isPatch ?? true), + }, }, }; }; diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/helpers.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/helpers.ts index 9e52b4a7414a6..bdd1107942cc1 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/helpers.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/helpers.ts @@ -15,7 +15,8 @@ export const getUpdateScript = ({ isPatch?: boolean; }) => { return { - source: ` + script: { + source: ` if (params.assignEmpty == true || params.containsKey('api_config')) { if (ctx._source.api_config != null) { if (params.assignEmpty == true || params.api_config.containsKey('connector_id')) { @@ -70,11 +71,12 @@ export const getUpdateScript = ({ } ctx._source.updated_at = params.updated_at; `, - lang: 'painless', - params: { - ...conversation, // when assigning undefined in painless, it will remove property and wil set it to null - // for patch we don't want to remove unspecified value in payload - assignEmpty: !(isPatch ?? true), + lang: 'painless', + params: { + ...conversation, // when assigning undefined in painless, it will remove property and wil set it to null + // for patch we don't want to remove unspecified value in payload + assignEmpty: !(isPatch ?? true), + }, }, }; }; diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/update_conversation.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/update_conversation.ts index 807fea2decd99..7e9ee336f6fe1 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/update_conversation.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/update_conversation.ts @@ -76,7 +76,7 @@ export const updateConversation = async ({ }, }, refresh: true, - script: getUpdateScript({ conversation: params, isPatch }), + script: getUpdateScript({ conversation: params, isPatch }).script, }); if (response.failures && response.failures.length > 0) { diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/create_knowledge_base_entry.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/create_knowledge_base_entry.ts index 23f73501b1056..09bb5b291ef9a 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/create_knowledge_base_entry.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/create_knowledge_base_entry.ts @@ -139,55 +139,11 @@ export const getUpdateScript = ({ entry: UpdateKnowledgeBaseEntrySchema; isPatch?: boolean; }) => { + // Cannot use script for updating documents with semantic_text fields return { - source: ` - if (params.assignEmpty == true || params.containsKey('name')) { - ctx._source.name = params.name; - } - if (params.assignEmpty == true || params.containsKey('type')) { - ctx._source.type = params.type; - } - if (params.assignEmpty == true || params.containsKey('users')) { - ctx._source.users = params.users; - } - if (params.assignEmpty == true || params.containsKey('query_description')) { - ctx._source.query_description = params.query_description; - } - if (params.assignEmpty == true || params.containsKey('input_schema')) { - ctx._source.input_schema = params.input_schema; - } - if (params.assignEmpty == true || params.containsKey('output_fields')) { - ctx._source.output_fields = params.output_fields; - } - if (params.assignEmpty == true || params.containsKey('kb_resource')) { - ctx._source.kb_resource = params.kb_resource; - } - if (params.assignEmpty == true || params.containsKey('required')) { - ctx._source.required = params.required; - } - if (params.assignEmpty == true || params.containsKey('source')) { - ctx._source.source = params.source; - } - if (params.assignEmpty == true || params.containsKey('text')) { - ctx._source.text = params.text; - } - if (params.assignEmpty == true || params.containsKey('description')) { - ctx._source.description = params.description; - } - if (params.assignEmpty == true || params.containsKey('field')) { - ctx._source.field = params.field; - } - if (params.assignEmpty == true || params.containsKey('index')) { - ctx._source.index = params.index; - } - ctx._source.updated_at = params.updated_at; - ctx._source.updated_by = params.updated_by; - `, - lang: 'painless', - params: { - ...entry, // when assigning undefined in painless, it will remove property and wil set it to null - // for patch we don't want to remove unspecified value in payload - assignEmpty: !(isPatch ?? true), + doc: { + ...entry, + semantic_text: entry.text, }, }; }; @@ -247,7 +203,7 @@ export const transformToCreateSchema = ({ required: entry.required ?? false, source: entry.source, text: entry.text, - vector: undefined, + semantic_text: entry.text, }; }; diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/field_maps_configuration.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/field_maps_configuration.ts index 0712664bbfeed..348efb5a18f7d 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/field_maps_configuration.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/field_maps_configuration.ts @@ -6,6 +6,8 @@ */ import { FieldMap } from '@kbn/data-stream-adapter'; +export const ASSISTANT_ELSER_INFERENCE_ID = 'elastic-security-ai-assistant-elser2'; + export const knowledgeBaseFieldMap: FieldMap = { '@timestamp': { type: 'date', @@ -169,6 +171,12 @@ export const knowledgeBaseFieldMapV2: FieldMap = { required: false, }, // Embeddings field + semantic_text: { + type: 'semantic_text', + array: false, + required: false, + inference_id: ASSISTANT_ELSER_INFERENCE_ID, + }, vector: { type: 'object', array: false, diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/helpers.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/helpers.ts index 59816b0b0c264..a19b3f0945086 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/helpers.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/helpers.ts @@ -46,7 +46,7 @@ export const getKBVectorSearchQuery = ({ filter?: QueryDslQueryContainer | undefined; kbResource?: string | undefined; modelId: string; - query: string; + query?: string; required?: boolean | undefined; user: AuthenticatedUser; v2KnowledgeBaseEnabled: boolean; @@ -114,20 +114,37 @@ export const getKBVectorSearchQuery = ({ ], }; - return { - bool: { - must: [ - { - text_expansion: { - 'vector.tokens': { - model_id: modelId, - model_text: query, - }, + let semanticTextFilter: + | Array<{ semantic: { field: string; query: string } }> + | Array<{ + text_expansion: { 'vector.tokens': { model_id: string; model_text: string } }; + }> = []; + + if (v2KnowledgeBaseEnabled && query) { + semanticTextFilter = [ + { + semantic: { + field: 'semantic_text', + query, + }, + }, + ]; + } else if (!v2KnowledgeBaseEnabled) { + semanticTextFilter = [ + { + text_expansion: { + 'vector.tokens': { + model_id: modelId, + model_text: query as string, }, }, - ...requiredFilter, - ...resourceFilter, - ], + }, + ]; + } + + return { + bool: { + must: [...semanticTextFilter, ...requiredFilter, ...resourceFilter], ...userFilter, filter, minimum_should_match: 1, diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/index.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/index.ts index 64e7b00089c08..333fbb796ddd9 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/index.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/index.ts @@ -8,6 +8,7 @@ import { MlTrainedModelDeploymentNodesStats, MlTrainedModelStats, + SearchTotalHits, } from '@elastic/elasticsearch/lib/api/types'; import type { MlPluginSetup } from '@kbn/ml-plugin/server'; import type { KibanaRequest } from '@kbn/core-http-server'; @@ -25,6 +26,8 @@ import pRetry from 'p-retry'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { StructuredTool } from '@langchain/core/tools'; import { ElasticsearchClient } from '@kbn/core/server'; +import { IndexPatternsFetcher } from '@kbn/data-views-plugin/server'; +import { map } from 'lodash'; import { AIAssistantDataClient, AIAssistantDataClientParams } from '..'; import { AssistantToolParams, GetElser } from '../../types'; import { @@ -38,6 +41,7 @@ import { transformESSearchToKnowledgeBaseEntry } from './transforms'; import { ESQL_DOCS_LOADED_QUERY, SECURITY_LABS_RESOURCE, + USER_RESOURCE, } from '../../routes/knowledge_base/constants'; import { getKBVectorSearchQuery, @@ -45,7 +49,11 @@ import { isModelAlreadyExistsError, } from './helpers'; import { getKBUserFilter } from '../../routes/knowledge_base/entries/utils'; -import { loadSecurityLabs } from '../../lib/langchain/content_loaders/security_labs_loader'; +import { + loadSecurityLabs, + getSecurityLabsDocsCount, +} from '../../lib/langchain/content_loaders/security_labs_loader'; +import { ASSISTANT_ELSER_INFERENCE_ID } from './field_maps_configuration'; /** * Params for when creating KbDataClient in Request Context Factory. Useful if needing to modify @@ -169,30 +177,83 @@ export class AIAssistantKnowledgeBaseDataClient extends AIAssistantDataClient { this.options.logger.debug(`Checking if ELSER model '${elserId}' is deployed...`); try { - const esClient = await this.options.elasticsearchClientPromise; - const getResponse = await esClient.ml.getTrainedModelsStats({ - model_id: elserId, - }); + if (this.isV2KnowledgeBaseEnabled) { + return await this.isInferenceEndpointExists(); + } else { + const esClient = await this.options.elasticsearchClientPromise; + const getResponse = await esClient.ml.getTrainedModelsStats({ + model_id: elserId, + }); - // For standardized way of checking deployment status see: https://github.com/elastic/elasticsearch/issues/106986 - const isReadyESS = (stats: MlTrainedModelStats) => - stats.deployment_stats?.state === 'started' && - stats.deployment_stats?.allocation_status.state === 'fully_allocated'; + // For standardized way of checking deployment status see: https://github.com/elastic/elasticsearch/issues/106986 + const isReadyESS = (stats: MlTrainedModelStats) => + stats.deployment_stats?.state === 'started' && + stats.deployment_stats?.allocation_status.state === 'fully_allocated'; - const isReadyServerless = (stats: MlTrainedModelStats) => - (stats.deployment_stats?.nodes as unknown as MlTrainedModelDeploymentNodesStats[]).some( - (node) => node.routing_state.routing_state === 'started' - ); + const isReadyServerless = (stats: MlTrainedModelStats) => + (stats.deployment_stats?.nodes as unknown as MlTrainedModelDeploymentNodesStats[])?.some( + (node) => node.routing_state.routing_state === 'started' + ); - return getResponse.trained_model_stats.some( - (stats) => isReadyESS(stats) || isReadyServerless(stats) - ); + return getResponse.trained_model_stats?.some( + (stats) => isReadyESS(stats) || isReadyServerless(stats) + ); + } } catch (e) { + this.options.logger.debug(`Error checking if ELSER model '${elserId}' is deployed: ${e}`); // Returns 404 if it doesn't exist return false; } }; + public isInferenceEndpointExists = async (): Promise => { + try { + const esClient = await this.options.elasticsearchClientPromise; + + return !!(await esClient.inference.get({ + inference_id: ASSISTANT_ELSER_INFERENCE_ID, + task_type: 'sparse_embedding', + })); + } catch (error) { + this.options.logger.debug( + `Error checking if Inference endpoint ${ASSISTANT_ELSER_INFERENCE_ID} exists: ${error}` + ); + return false; + } + }; + + public createInferenceEndpoint = async () => { + const elserId = await this.options.getElserId(); + this.options.logger.debug(`Deploying ELSER model '${elserId}'...`); + try { + const esClient = await this.options.elasticsearchClientPromise; + if (this.isV2KnowledgeBaseEnabled) { + await esClient.inference.put({ + task_type: 'sparse_embedding', + inference_id: ASSISTANT_ELSER_INFERENCE_ID, + inference_config: { + service: 'elasticsearch', + service_settings: { + adaptive_allocations: { + enabled: true, + min_number_of_allocations: 0, + max_number_of_allocations: 8, + }, + num_threads: 1, + model_id: elserId, + }, + task_settings: {}, + }, + }); + } + } catch (error) { + this.options.logger.error( + `Error creating inference endpoint for ELSER model '${elserId}':\n${error}` + ); + throw new Error(`Error creating inference endpoint for ELSER model '${elserId}':\n${error}`); + } + }; + /** * Downloads and deploys recommended ELSER (if not already), then loads ES|QL docs * @@ -208,9 +269,11 @@ export class AIAssistantKnowledgeBaseDataClient extends AIAssistantDataClient { public setupKnowledgeBase = async ({ soClient, v2KnowledgeBaseEnabled = true, + ignoreSecurityLabs = false, }: { soClient: SavedObjectsClientContract; v2KnowledgeBaseEnabled?: boolean; + ignoreSecurityLabs?: boolean; }): Promise => { if (this.options.getIsKBSetupInProgress()) { this.options.logger.debug('Knowledge Base setup already in progress'); @@ -238,8 +301,22 @@ export class AIAssistantKnowledgeBaseDataClient extends AIAssistantDataClient { `Removed ${legacyESQL?.total} ESQL knowledge base docs from knowledge base data stream: ${this.indexTemplateAndPattern.alias}.` ); } + // Delete any existing Security Labs content + const securityLabsDocs = await esClient.deleteByQuery({ + index: this.indexTemplateAndPattern.alias, + query: { + bool: { + must: [{ terms: { kb_resource: [SECURITY_LABS_RESOURCE] } }], + }, + }, + }); + if (securityLabsDocs?.total) { + this.options.logger.info( + `Removed ${securityLabsDocs?.total} Security Labs knowledge base docs from knowledge base data stream: ${this.indexTemplateAndPattern.alias}.` + ); + } } catch (e) { - this.options.logger.info('No legacy ESQL knowledge base docs to delete'); + this.options.logger.info('No legacy ESQL or Security Labs knowledge base docs to delete'); } } @@ -259,24 +336,39 @@ export class AIAssistantKnowledgeBaseDataClient extends AIAssistantDataClient { this.options.logger.debug(`ELSER model '${elserId}' is already installed`); } - const isDeployed = await this.isModelDeployed(); - if (!isDeployed) { - await this.deployModel(); - await pRetry( - async () => - (await this.isModelDeployed()) - ? Promise.resolve() - : Promise.reject(new Error('Model not deployed')), - { minTimeout: 2000, retries: 10 } - ); - this.options.logger.debug(`ELSER model '${elserId}' successfully deployed!`); + if (!this.isV2KnowledgeBaseEnabled) { + const isDeployed = await this.isModelDeployed(); + if (!isDeployed) { + await this.deployModel(); + await pRetry( + async () => + (await this.isModelDeployed()) + ? Promise.resolve() + : Promise.reject(new Error('Model not deployed')), + { minTimeout: 2000, retries: 10 } + ); + this.options.logger.debug(`ELSER model '${elserId}' successfully deployed!`); + } else { + this.options.logger.debug(`ELSER model '${elserId}' is already deployed`); + } } else { - this.options.logger.debug(`ELSER model '${elserId}' is already deployed`); + const inferenceExists = await this.isInferenceEndpointExists(); + if (!inferenceExists) { + await this.createInferenceEndpoint(); + + this.options.logger.debug( + `Inference endpoint for ELSER model '${elserId}' successfully deployed!` + ); + } else { + this.options.logger.debug( + `Inference endpoint for ELSER model '${elserId}' is already deployed` + ); + } } this.options.logger.debug(`Checking if Knowledge Base docs have been loaded...`); - if (v2KnowledgeBaseEnabled) { + if (v2KnowledgeBaseEnabled && !ignoreSecurityLabs) { const labsDocsLoaded = await this.isSecurityLabsDocsLoaded(); if (!labsDocsLoaded) { this.options.logger.debug(`Loading Security Labs KB docs...`); @@ -289,8 +381,9 @@ export class AIAssistantKnowledgeBaseDataClient extends AIAssistantDataClient { this.options.setIsKBSetupInProgress(false); this.options.logger.error(`Error setting up Knowledge Base: ${e.message}`); throw new Error(`Error setting up Knowledge Base: ${e.message}`); + } finally { + this.options.setIsKBSetupInProgress(false); } - this.options.setIsKBSetupInProgress(false); }; /** @@ -385,15 +478,87 @@ export class AIAssistantKnowledgeBaseDataClient extends AIAssistantDataClient { }; /** - * Returns if Security Labs KB docs have been loaded + * Returns if user's KB docs exists + */ + + public isUserDataExists = async (): Promise => { + const user = this.options.currentUser; + if (user == null) { + throw new Error( + 'Authenticated user not found! Ensure kbDataClient was initialized from a request.' + ); + } + + const esClient = await this.options.elasticsearchClientPromise; + const modelId = await this.options.getElserId(); + + try { + const vectorSearchQuery = getKBVectorSearchQuery({ + kbResource: USER_RESOURCE, + required: false, + user, + modelId, + v2KnowledgeBaseEnabled: this.options.v2KnowledgeBaseEnabled, + }); + + const result = await esClient.search({ + index: this.indexTemplateAndPattern.alias, + size: 0, + query: vectorSearchQuery, + track_total_hits: true, + }); + + return !!(result.hits?.total as SearchTotalHits).value; + } catch (e) { + this.options.logger.debug(`Error checking if user's KB docs exist: ${e.message}`); + return false; + } + }; + + /** + * Returns if allSecurity Labs KB docs have been loaded */ public isSecurityLabsDocsLoaded = async (): Promise => { - const securityLabsDocs = await this.getKnowledgeBaseDocumentEntries({ - query: '', - kbResource: SECURITY_LABS_RESOURCE, - required: false, - }); - return securityLabsDocs.length > 0; + const user = this.options.currentUser; + if (user == null) { + throw new Error( + 'Authenticated user not found! Ensure kbDataClient was initialized from a request.' + ); + } + + const expectedDocsCount = await getSecurityLabsDocsCount({ logger: this.options.logger }); + + const esClient = await this.options.elasticsearchClientPromise; + const modelId = await this.options.getElserId(); + + try { + const vectorSearchQuery = getKBVectorSearchQuery({ + kbResource: SECURITY_LABS_RESOURCE, + required: false, + user, + modelId, + v2KnowledgeBaseEnabled: this.options.v2KnowledgeBaseEnabled, + }); + + const result = await esClient.search({ + index: this.indexTemplateAndPattern.alias, + size: 0, + query: vectorSearchQuery, + track_total_hits: true, + }); + + const existingDocs = (result.hits?.total as SearchTotalHits).value; + + if (existingDocs !== expectedDocsCount) { + this.options.logger.debug( + `Security Labs docs are not loaded, existing docs: ${existingDocs}, expected docs: ${expectedDocsCount}` + ); + } + return existingDocs === expectedDocsCount; + } catch (e) { + this.options.logger.info(`Error checking if Security Labs docs are loaded: ${e.message}`); + return false; + } }; /** @@ -423,10 +588,10 @@ export class AIAssistantKnowledgeBaseDataClient extends AIAssistantDataClient { const vectorSearchQuery = getKBVectorSearchQuery({ filter, kbResource, - modelId, query, required, user, + modelId, v2KnowledgeBaseEnabled: this.options.v2KnowledgeBaseEnabled, }); @@ -576,7 +741,9 @@ export class AIAssistantKnowledgeBaseDataClient extends AIAssistantDataClient { } try { - const elserId = await this.options.getElserId(); + const elserId = this.isV2KnowledgeBaseEnabled + ? ASSISTANT_ELSER_INFERENCE_ID + : await this.options.getElserId(); const userFilter = getKBUserFilter(user); const results = await this.findDocuments({ // Note: This is a magic number to set some upward bound as to not blow the context with too @@ -595,14 +762,21 @@ export class AIAssistantKnowledgeBaseDataClient extends AIAssistantDataClient { if (results) { const entries = transformESSearchToKnowledgeBaseEntry(results.data) as IndexEntry[]; - return entries.map((indexEntry) => { - return getStructuredToolForIndexEntry({ - indexEntry, - esClient, - logger: this.options.logger, - elserId, - }); - }); + const indexPatternFetcher = new IndexPatternsFetcher(esClient); + const existingIndices = await indexPatternFetcher.getExistingIndices(map(entries, 'index')); + return ( + entries + // Filter out any IndexEntries that don't have an existing index + .filter((entry) => existingIndices.includes(entry.index)) + .map((indexEntry) => { + return getStructuredToolForIndexEntry({ + indexEntry, + esClient, + logger: this.options.logger, + elserId, + }); + }) + ); } } catch (e) { this.options.logger.error(`kbDataClient.getAssistantTools() - Failed to fetch IndexEntries`); diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/ingest_pipeline.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/ingest_pipeline.ts index e11840b94e660..8f459848af420 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/ingest_pipeline.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/ingest_pipeline.ts @@ -5,22 +5,31 @@ * 2.0. */ -// TODO: Ensure old pipeline is updated/replaced -export const knowledgeBaseIngestPipeline = ({ id, modelId }: { id: string; modelId: string }) => ({ +export const knowledgeBaseIngestPipeline = ({ + id, + modelId, + v2KnowledgeBaseEnabled, +}: { + id: string; + modelId: string; + v2KnowledgeBaseEnabled: boolean; +}) => ({ id, description: 'Embedding pipeline for Elastic AI Assistant ELSER Knowledge Base', - processors: [ - { - inference: { - if: 'ctx?.text != null', - model_id: modelId, - input_output: [ - { - input_field: 'text', - output_field: 'vector.tokens', + processors: !v2KnowledgeBaseEnabled + ? [ + { + inference: { + if: 'ctx?.text != null', + model_id: modelId, + input_output: [ + { + input_field: 'text', + output_field: 'vector.tokens', + }, + ], }, - ], - }, - }, - ], + }, + ] + : [], }); diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/types.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/types.ts index 3de1a15d79b2a..443d03941ccdd 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/types.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/types.ts @@ -27,6 +27,7 @@ export interface EsDocumentEntry { required: boolean; source: string; text: string; + semantic_text?: string; vector?: { tokens: Record; model_id: string; @@ -99,6 +100,7 @@ export interface UpdateKnowledgeBaseEntrySchema { required?: boolean; source?: string; text?: string; + semantic_text?: string; vector?: { tokens: Record; model_id: string; @@ -135,6 +137,7 @@ export interface CreateKnowledgeBaseEntrySchema { required?: boolean; source?: string; text?: string; + semantic_text?: string; vector?: { tokens: Record; model_id: string; diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/prompts/helpers.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/prompts/helpers.ts index a4534972c8478..eb71270127b2a 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/prompts/helpers.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/prompts/helpers.ts @@ -143,7 +143,8 @@ export const getUpdateScript = ({ isPatch?: boolean; }) => { return { - source: ` + script: { + source: ` if (params.assignEmpty == true || params.containsKey('content')) { ctx._source.content = params.content; } @@ -158,11 +159,12 @@ export const getUpdateScript = ({ } ctx._source.updated_at = params.updated_at; `, - lang: 'painless', - params: { - ...prompt, // when assigning undefined in painless, it will remove property and wil set it to null - // for patch we don't want to remove unspecified value in payload - assignEmpty: !(isPatch ?? true), + lang: 'painless', + params: { + ...prompt, // when assigning undefined in painless, it will remove property and wil set it to null + // for patch we don't want to remove unspecified value in payload + assignEmpty: !(isPatch ?? true), + }, }, }; }; diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_service/create_resource_installation_helper.test.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_service/create_resource_installation_helper.test.ts index 8e34581332ff6..85f6de83592ae 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_service/create_resource_installation_helper.test.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_service/create_resource_installation_helper.test.ts @@ -141,7 +141,7 @@ describe('createResourceInstallationHelper', () => { async () => (await getContextInitialized(helper)) === false ); - expect(logger.error).toHaveBeenCalledWith(`Error initializing resources test1 - fail`); + expect(logger.warn).toHaveBeenCalledWith(`Error initializing resources test1 - fail`); expect(await helper.getInitializedResources('test1')).toEqual({ result: false, error: `fail`, @@ -204,7 +204,7 @@ describe('createResourceInstallationHelper', () => { async () => (await getContextInitialized(helper)) === false ); - expect(logger.error).toHaveBeenCalledWith(`Error initializing resources default - first error`); + expect(logger.warn).toHaveBeenCalledWith(`Error initializing resources default - first error`); expect(await helper.getInitializedResources(DEFAULT_NAMESPACE_STRING)).toEqual({ result: false, error: `first error`, @@ -221,9 +221,7 @@ describe('createResourceInstallationHelper', () => { return logger.error.mock.calls.length === 1; }); - expect(logger.error).toHaveBeenCalledWith( - `Error initializing resources default - second error` - ); + expect(logger.warn).toHaveBeenCalledWith(`Error initializing resources default - second error`); // the second retry is throttled so this is never called expect(logger.info).not.toHaveBeenCalledWith('test1_default successfully retried'); diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_service/create_resource_installation_helper.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_service/create_resource_installation_helper.ts index 39e0e69a8fc49..e8d1f1eb1d85d 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_service/create_resource_installation_helper.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_service/create_resource_installation_helper.ts @@ -65,7 +65,7 @@ export function createResourceInstallationHelper( return errorResult(commonInitError); } } catch (err) { - logger.error(`Error initializing resources ${namespace} - ${err.message}`); + logger.warn(`Error initializing resources ${namespace} - ${err.message}`); return errorResult(err.message); } }; @@ -113,7 +113,7 @@ export function createResourceInstallationHelper( const key = namespace; return ( initializedResources.has(key) - ? initializedResources.get(key) + ? await initializedResources.get(key) : errorResult(`Unrecognized spaceId ${key}`) ) as InitializationPromise; }, diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_service/helpers.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_service/helpers.ts index 07da930320712..93338174364fc 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_service/helpers.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_service/helpers.ts @@ -54,6 +54,7 @@ interface CreatePipelineParams { esClient: ElasticsearchClient; id: string; modelId: string; + v2KnowledgeBaseEnabled: boolean; } /** @@ -70,12 +71,14 @@ export const createPipeline = async ({ esClient, id, modelId, + v2KnowledgeBaseEnabled, }: CreatePipelineParams): Promise => { try { const response = await esClient.ingest.putPipeline( knowledgeBaseIngestPipeline({ id, modelId, + v2KnowledgeBaseEnabled, }) ); diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_service/index.test.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_service/index.test.ts index 8bd1173e93d89..23a1a55564415 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_service/index.test.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_service/index.test.ts @@ -18,11 +18,17 @@ import { AIAssistantService, AIAssistantServiceOpts } from '.'; import { retryUntil } from './create_resource_installation_helper.test'; import { mlPluginMock } from '@kbn/ml-plugin/public/mocks'; import type { MlPluginSetup } from '@kbn/ml-plugin/server'; +import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; jest.mock('../ai_assistant_data_clients/conversations', () => ({ AIAssistantConversationsDataClient: jest.fn(), })); +const licensing = Promise.resolve( + licensingMock.createRequestHandlerContext({ + license: { type: 'enterprise' }, + }) +); let logger: ReturnType<(typeof loggingSystemMock)['createLogger']>; const clusterClient = elasticsearchServiceMock.createClusterClient().asInternalUser; @@ -191,6 +197,7 @@ describe('AI Assistant Service', () => { logger, spaceId: 'default', currentUser: mockUser1, + licensing, }); expect(AIAssistantConversationsDataClient).toHaveBeenCalledWith({ @@ -221,6 +228,7 @@ describe('AI Assistant Service', () => { logger, spaceId: 'default', currentUser: mockUser1, + licensing, }); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalled(); @@ -274,11 +282,13 @@ describe('AI Assistant Service', () => { logger, spaceId: 'default', currentUser: mockUser1, + licensing, }), assistantService.createAIAssistantConversationsDataClient({ logger, spaceId: 'default', currentUser: mockUser1, + licensing, }), ]); @@ -340,6 +350,7 @@ describe('AI Assistant Service', () => { logger, spaceId: 'default', currentUser: mockUser1, + licensing, }); expect(AIAssistantConversationsDataClient).toHaveBeenCalledWith({ @@ -400,6 +411,7 @@ describe('AI Assistant Service', () => { logger, spaceId: 'default', currentUser: mockUser1, + licensing, }); }; @@ -472,6 +484,7 @@ describe('AI Assistant Service', () => { logger, spaceId: 'default', currentUser: mockUser1, + licensing, }); }; @@ -513,6 +526,7 @@ describe('AI Assistant Service', () => { logger, spaceId: 'test', currentUser: mockUser1, + licensing, }); expect(clusterClient.indices.putIndexTemplate).not.toHaveBeenCalled(); @@ -560,6 +574,7 @@ describe('AI Assistant Service', () => { logger, spaceId: 'test', currentUser: mockUser1, + licensing, }); expect(clusterClient.indices.putIndexTemplate).not.toHaveBeenCalled(); @@ -607,6 +622,7 @@ describe('AI Assistant Service', () => { logger, spaceId: 'test', currentUser: mockUser1, + licensing, }); expect(AIAssistantConversationsDataClient).not.toHaveBeenCalled(); @@ -752,6 +768,7 @@ describe('AI Assistant Service', () => { logger, spaceId: 'default', currentUser: mockUser1, + licensing, }); await retryUntil( diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_service/index.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_service/index.ts index bfdf8b96f44b0..15274f2323259 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_service/index.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_service/index.ts @@ -11,6 +11,7 @@ import type { AuthenticatedUser, Logger, ElasticsearchClient } from '@kbn/core/s import type { TaskManagerSetupContract } from '@kbn/task-manager-plugin/server'; import type { MlPluginSetup } from '@kbn/ml-plugin/server'; import { Subject } from 'rxjs'; +import { LicensingApiRequestHandlerContext } from '@kbn/licensing-plugin/server'; import { attackDiscoveryFieldMap } from '../lib/attack_discovery/persistence/field_maps_configuration/field_maps_configuration'; import { getDefaultAnonymizationFields } from '../../common/anonymization'; import { AssistantResourceNames, GetElser } from '../types'; @@ -36,6 +37,7 @@ import { } from '../ai_assistant_data_clients/knowledge_base'; import { AttackDiscoveryDataClient } from '../lib/attack_discovery/persistence'; import { createGetElserId, createPipeline, pipelineExists } from './helpers'; +import { hasAIAssistantLicense } from '../routes/helpers'; const TOTAL_FIELDS_LIMIT = 2500; @@ -56,6 +58,7 @@ export interface CreateAIAssistantClientParams { logger: Logger; spaceId: string; currentUser: AuthenticatedUser | null; + licensing: Promise; } export type CreateDataStream = (params: { @@ -97,7 +100,7 @@ export class AIAssistantService { this.knowledgeBaseDataStream = this.createDataStream({ resource: 'knowledgeBase', kibanaVersion: options.kibanaVersion, - fieldMap: knowledgeBaseFieldMap, // TODO: use V2 if FF is enabled + fieldMap: knowledgeBaseFieldMap, }); this.promptsDataStream = this.createDataStream({ resource: 'prompts', @@ -151,7 +154,9 @@ export class AIAssistantService { name: this.resourceNames.indexTemplate[resource], componentTemplateRefs: [this.resourceNames.componentTemplate[resource]], // Apply `default_pipeline` if pipeline exists for resource - ...(resource in this.resourceNames.pipelines + ...(resource in this.resourceNames.pipelines && + // Remove this param and initialization when the `assistantKnowledgeBaseByDefault` feature flag is removed + !(resource === 'knowledgeBase' && this.v2KnowledgeBaseEnabled) ? { template: { settings: { @@ -202,7 +207,12 @@ export class AIAssistantService { id: this.resourceNames.pipelines.knowledgeBase, }); // TODO: When FF is removed, ensure pipeline is re-created for those upgrading - if (!pipelineCreated || this.v2KnowledgeBaseEnabled) { + if ( + // Install for v1 + (!this.v2KnowledgeBaseEnabled && !pipelineCreated) || + // Upgrade from v1 to v2 + (pipelineCreated && this.v2KnowledgeBaseEnabled) + ) { this.options.logger.debug( `Installing ingest pipeline - ${this.resourceNames.pipelines.knowledgeBase}` ); @@ -210,6 +220,7 @@ export class AIAssistantService { esClient, id: this.resourceNames.pipelines.knowledgeBase, modelId: await this.getElserId(), + v2KnowledgeBaseEnabled: this.v2KnowledgeBaseEnabled, }); this.options.logger.debug(`Installed ingest pipeline: ${response}`); @@ -237,7 +248,7 @@ export class AIAssistantService { pluginStop$: this.options.pluginStop$, }); } catch (error) { - this.options.logger.error(`Error initializing AI assistant resources: ${error.message}`); + this.options.logger.warn(`Error initializing AI assistant resources: ${error.message}`); this.initialized = false; this.isInitializing = false; return errorResult(error.message); @@ -282,6 +293,8 @@ export class AIAssistantService { }; private async checkResourcesInstallation(opts: CreateAIAssistantClientParams) { + const licensing = await opts.licensing; + if (!hasAIAssistantLicense(licensing.license)) return null; // Check if resources installation has succeeded const { result: initialized, error } = await this.getSpaceResourcesInitializationPromise( opts.spaceId @@ -502,7 +515,7 @@ export class AIAssistantService { await this.createDefaultAnonymizationFields(spaceId); } } catch (error) { - this.options.logger.error( + this.options.logger.warn( `Error initializing AI assistant namespace level resources: ${error.message}` ); throw error; diff --git a/x-pack/plugins/elastic_assistant/server/lib/data_stream/documents_data_writer.ts b/x-pack/plugins/elastic_assistant/server/lib/data_stream/documents_data_writer.ts index 32b579fdeb71a..08892038a58b7 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/data_stream/documents_data_writer.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/data_stream/documents_data_writer.ts @@ -34,7 +34,10 @@ interface BulkParams { documentsToCreate?: TCreateParams[]; documentsToUpdate?: TUpdateParams[]; documentsToDelete?: string[]; - getUpdateScript?: (document: TUpdateParams, updatedAt: string) => Script; + getUpdateScript?: ( + document: TUpdateParams, + updatedAt: string + ) => { script?: Script; doc?: TUpdateParams }; authenticatedUser?: AuthenticatedUser; } @@ -73,7 +76,7 @@ export class DocumentsDataWriter implements DocumentsDataWriter { body: await this.buildBulkOperations(params), }, { - // Increasing timout to 2min as KB docs were failing to load after 30s + // Increasing timeout to 2min as KB docs were failing to load after 30s requestTimeout: 120000, } ); @@ -151,7 +154,10 @@ export class DocumentsDataWriter implements DocumentsDataWriter { private getUpdateDocumentsQuery = async ( documentsToUpdate: TUpdateParams[], - getUpdateScript: (document: TUpdateParams, updatedAt: string) => Script, + getUpdateScript: ( + document: TUpdateParams, + updatedAt: string + ) => { script?: Script; doc?: TUpdateParams }, authenticatedUser?: AuthenticatedUser ) => { const updatedAt = new Date().toISOString(); @@ -196,10 +202,7 @@ export class DocumentsDataWriter implements DocumentsDataWriter { _source: true, }, }, - { - script: getUpdateScript(document, updatedAt), - upsert: { counter: 1 }, - }, + getUpdateScript(document, updatedAt), ]); }; diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/content_loaders/security_labs_loader.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/content_loaders/security_labs_loader.ts index 10566b3e5a1d5..f37e20df2bd98 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/content_loaders/security_labs_loader.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/content_loaders/security_labs_loader.ts @@ -5,13 +5,14 @@ * 2.0. */ +import globby from 'globby'; import { Logger } from '@kbn/core/server'; import { DirectoryLoader } from 'langchain/document_loaders/fs/directory'; import { TextLoader } from 'langchain/document_loaders/fs/text'; import { resolve } from 'path'; import { Document } from 'langchain/document'; import { Metadata } from '@kbn/elastic-assistant-common'; - +import pMap from 'p-map'; import { addRequiredKbResourceMetadata } from './add_required_kb_resource_metadata'; import { SECURITY_LABS_RESOURCE } from '../../../routes/knowledge_base/constants'; import { AIAssistantKnowledgeBaseDataClient } from '../../../ai_assistant_data_clients/knowledge_base'; @@ -42,10 +43,22 @@ export const loadSecurityLabs = async ( logger.info(`Loading ${docs.length} Security Labs docs into the Knowledge Base`); - const response = await kbDataClient.addKnowledgeBaseDocuments({ - documents: docs, - global: true, - }); + /** + * Ingest Security Labs docs into the Knowledge Base one by one to avoid blocking + * Inference Endpoint for too long + */ + + const response = ( + await pMap( + docs, + (singleDoc) => + kbDataClient.addKnowledgeBaseDocuments({ + documents: [singleDoc], + global: true, + }), + { concurrency: 1 } + ) + ).flat(); logger.info(`Loaded ${response?.length ?? 0} Security Labs docs into the Knowledge Base`); @@ -55,3 +68,13 @@ export const loadSecurityLabs = async ( return false; } }; + +export const getSecurityLabsDocsCount = async ({ logger }: { logger: Logger }): Promise => { + try { + return (await globby(`${resolve(__dirname, '../../../knowledge_base/security_labs')}/**/*.md`)) + ?.length; + } catch (e) { + logger.error(`Failed to get Security Labs source docs count\n${e}`); + return 0; + } +}; diff --git a/x-pack/plugins/elastic_assistant/server/routes/anonymization_fields/bulk_actions_route.ts b/x-pack/plugins/elastic_assistant/server/routes/anonymization_fields/bulk_actions_route.ts index 5464756739c08..9aedffae5cfb5 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/anonymization_fields/bulk_actions_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/anonymization_fields/bulk_actions_route.ts @@ -38,7 +38,7 @@ import { EsAnonymizationFieldsSchema, UpdateAnonymizationFieldSchema, } from '../../ai_assistant_data_clients/anonymization_fields/types'; -import { UPGRADE_LICENSE_MESSAGE, hasAIAssistantLicense } from '../helpers'; +import { performChecks } from '../helpers'; export interface BulkOperationError { message: string; @@ -162,22 +162,18 @@ export const bulkActionAnonymizationFieldsRoute = ( request.events.completed$.subscribe(() => abortController.abort()); try { const ctx = await context.resolve(['core', 'elasticAssistant', 'licensing']); - const license = ctx.licensing.license; - if (!hasAIAssistantLicense(license)) { - return response.forbidden({ - body: { - message: UPGRADE_LICENSE_MESSAGE, - }, - }); - } + // Perform license and authenticated user checks + const checkResponse = performChecks({ + context: ctx, + request, + response, + }); - const authenticatedUser = ctx.elasticAssistant.getCurrentUser(); - if (authenticatedUser == null) { - return assistantResponse.error({ - body: `Authenticated user not found`, - statusCode: 401, - }); + if (!checkResponse.isSuccess) { + return checkResponse.response; } + const authenticatedUser = checkResponse.currentUser; + const dataClient = await ctx.elasticAssistant.getAIAssistantAnonymizationFieldsDataClient(); @@ -199,7 +195,7 @@ export const bulkActionAnonymizationFieldsRoute = ( } const writer = await dataClient?.getWriter(); - const changedAt = new Date().toISOString(); + const createdAt = new Date().toISOString(); const { errors, docs_created: docsCreated, @@ -207,12 +203,12 @@ export const bulkActionAnonymizationFieldsRoute = ( docs_deleted: docsDeleted, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion } = await writer!.bulk({ - documentsToCreate: body.create?.map((f) => - transformToCreateScheme(authenticatedUser, changedAt, f) + documentsToCreate: body.create?.map((doc) => + transformToCreateScheme(authenticatedUser, createdAt, doc) ), documentsToDelete: body.delete?.ids, - documentsToUpdate: body.update?.map((f) => - transformToUpdateScheme(authenticatedUser, changedAt, f) + documentsToUpdate: body.update?.map((doc) => + transformToUpdateScheme(authenticatedUser, createdAt, doc) ), getUpdateScript: (document: UpdateAnonymizationFieldSchema) => getUpdateScript({ anonymizationField: document, isPatch: true }), diff --git a/x-pack/plugins/elastic_assistant/server/routes/anonymization_fields/find_route.test.ts b/x-pack/plugins/elastic_assistant/server/routes/anonymization_fields/find_route.test.ts index 4659503261a6e..7c2b1d330a3db 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/anonymization_fields/find_route.test.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/anonymization_fields/find_route.test.ts @@ -12,6 +12,7 @@ import { requestContextMock } from '../../__mocks__/request_context'; import { getFindAnonymizationFieldsResultWithSingleHit } from '../../__mocks__/response'; import { findAnonymizationFieldsRoute } from './find_route'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; +import type { AuthenticatedUser } from '@kbn/core-security-common'; describe('Find user anonymization fields route', () => { let server: ReturnType; @@ -21,19 +22,26 @@ describe('Find user anonymization fields route', () => { beforeEach(async () => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); + const mockUser1 = { + username: 'my_username', + authentication_realm: { + type: 'my_realm_type', + name: 'my_realm_name', + }, + } as AuthenticatedUser; clients.elasticAssistant.getAIAssistantAnonymizationFieldsDataClient.findDocuments.mockResolvedValue( Promise.resolve(getFindAnonymizationFieldsResultWithSingleHit()) ); - clients.elasticAssistant.getCurrentUser.mockResolvedValue({ + context.elasticAssistant.getCurrentUser.mockReturnValue({ username: 'my_username', authentication_realm: { type: 'my_realm_type', name: 'my_realm_name', }, - }); + } as AuthenticatedUser); logger = loggingSystemMock.createLogger(); - + context.elasticAssistant.getCurrentUser.mockReturnValue(mockUser1); findAnonymizationFieldsRoute(server.router, logger); }); diff --git a/x-pack/plugins/elastic_assistant/server/routes/anonymization_fields/find_route.ts b/x-pack/plugins/elastic_assistant/server/routes/anonymization_fields/find_route.ts index 904a80d6a3ea4..061dd9ff3eac6 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/anonymization_fields/find_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/anonymization_fields/find_route.ts @@ -22,7 +22,7 @@ import { ElasticAssistantPluginRouter } from '../../types'; import { buildResponse } from '../utils'; import { EsAnonymizationFieldsSchema } from '../../ai_assistant_data_clients/anonymization_fields/types'; import { transformESSearchToAnonymizationFields } from '../../ai_assistant_data_clients/anonymization_fields/helpers'; -import { UPGRADE_LICENSE_MESSAGE, hasAIAssistantLicense } from '../helpers'; +import { performChecks } from '../helpers'; export const findAnonymizationFieldsRoute = ( router: ElasticAssistantPluginRouter, @@ -55,14 +55,16 @@ export const findAnonymizationFieldsRoute = ( try { const { query } = request; const ctx = await context.resolve(['core', 'elasticAssistant', 'licensing']); - const license = ctx.licensing.license; - if (!hasAIAssistantLicense(license)) { - return response.forbidden({ - body: { - message: UPGRADE_LICENSE_MESSAGE, - }, - }); + // Perform license and authenticated user checks + const checkResponse = performChecks({ + context: ctx, + request, + response, + }); + if (!checkResponse.isSuccess) { + return checkResponse.response; } + const dataClient = await ctx.elasticAssistant.getAIAssistantAnonymizationFieldsDataClient(); diff --git a/x-pack/plugins/elastic_assistant/server/routes/chat/chat_complete_route.test.ts b/x-pack/plugins/elastic_assistant/server/routes/chat/chat_complete_route.test.ts index 0b05bb2875cb6..f03a3394cdaac 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/chat/chat_complete_route.test.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/chat/chat_complete_route.test.ts @@ -32,7 +32,17 @@ const actionsClient = actionsClientMock.create(); jest.mock('../../lib/build_response', () => ({ buildResponse: jest.fn().mockImplementation((x) => x), })); -jest.mock('../helpers'); + +jest.mock('../helpers', () => { + const original = jest.requireActual('../helpers'); + + return { + ...original, + appendAssistantMessageToConversation: jest.fn(), + createConversationWithUserInput: jest.fn(), + langChainExecute: jest.fn(), + }; +}); const mockAppendAssistantMessageToConversation = appendAssistantMessageToConversation as jest.Mock; const mockLangChainExecute = langChainExecute as jest.Mock; @@ -280,6 +290,7 @@ describe('chatCompleteRoute', () => { actionTypeId: '.gen-ai', model: 'gpt-4', assistantStreamingEnabled: false, + isEnabledKnowledgeBase: false, }); }), }; diff --git a/x-pack/plugins/elastic_assistant/server/routes/chat/chat_complete_route.ts b/x-pack/plugins/elastic_assistant/server/routes/chat/chat_complete_route.ts index 47f6f1a486957..c6eb81dd86ebd 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/chat/chat_complete_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/chat/chat_complete_route.ts @@ -25,6 +25,9 @@ import { buildResponse } from '../../lib/build_response'; import { appendAssistantMessageToConversation, createConversationWithUserInput, + DEFAULT_PLUGIN_NAME, + getIsKnowledgeBaseInstalled, + getPluginNameFromRequest, langChainExecute, performChecks, } from '../helpers'; @@ -63,22 +66,20 @@ export const chatCompleteRoute = ( const assistantResponse = buildResponse(response); let telemetry; let actionTypeId; + const ctx = await context.resolve(['core', 'elasticAssistant', 'licensing']); + const logger: Logger = ctx.elasticAssistant.logger; try { - const ctx = await context.resolve(['core', 'elasticAssistant', 'licensing']); - const logger: Logger = ctx.elasticAssistant.logger; telemetry = ctx.elasticAssistant.telemetry; const inference = ctx.elasticAssistant.inference; // Perform license and authenticated user checks const checkResponse = performChecks({ - authenticatedUser: true, context: ctx, - license: true, request, response, }); - if (checkResponse) { - return checkResponse; + if (!checkResponse.isSuccess) { + return checkResponse.response; } const conversationsDataClient = @@ -221,6 +222,19 @@ export const chatCompleteRoute = ( }); } catch (err) { const error = transformError(err as Error); + const pluginName = getPluginNameFromRequest({ + request, + defaultPluginName: DEFAULT_PLUGIN_NAME, + logger, + }); + const v2KnowledgeBaseEnabled = + ctx.elasticAssistant.getRegisteredFeatures(pluginName).assistantKnowledgeBaseByDefault; + const kbDataClient = + (await ctx.elasticAssistant.getAIAssistantKnowledgeBaseDataClient({ + v2KnowledgeBaseEnabled, + })) ?? undefined; + const isKnowledgeBaseInstalled = await getIsKnowledgeBaseInstalled(kbDataClient); + telemetry?.reportEvent(INVOKE_ASSISTANT_ERROR_EVENT.eventType, { actionTypeId: actionTypeId ?? '', model: request.body.model, @@ -228,6 +242,7 @@ export const chatCompleteRoute = ( // TODO rm actionTypeId check when llmClass for bedrock streaming is implemented // tracked here: https://github.com/elastic/security-team/issues/7363 assistantStreamingEnabled: request.body.isStream ?? false, + isEnabledKnowledgeBase: isKnowledgeBaseInstalled, }); return assistantResponse.error({ body: error.message, diff --git a/x-pack/plugins/elastic_assistant/server/routes/evaluate/get_evaluate.ts b/x-pack/plugins/elastic_assistant/server/routes/evaluate/get_evaluate.ts index 41b455a73598b..dd7462696621b 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/evaluate/get_evaluate.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/evaluate/get_evaluate.ts @@ -48,15 +48,14 @@ export const getEvaluateRoute = (router: IRouter(result); }; @@ -561,55 +560,66 @@ export const updateConversationWithUserInput = async ({ }; interface PerformChecksParams { - authenticatedUser?: boolean; capability?: AssistantFeatureKey; context: AwaitedProperties< Pick >; - license?: boolean; request: KibanaRequest; response: KibanaResponseFactory; } /** - * Helper to perform checks for authenticated user, capability, and license. Perform all or one - * of the checks by providing relevant optional params. Check order is license, authenticated user, - * then capability. + * Helper to perform checks for authenticated user, license, and optionally capability. + * Check order is license, authenticated user, then capability. + * + * Returns either a successful check with an AuthenticatedUser or + * an unsuccessful check with an error IKibanaResponse. * - * @param authenticatedUser - Whether to check for an authenticated user * @param capability - Specific capability to check if enabled, e.g. `assistantModelEvaluation` * @param context - Route context - * @param license - Whether to check for a valid license * @param request - Route KibanaRequest * @param response - Route KibanaResponseFactory + * @returns PerformChecks */ + +type PerformChecks = + | { + isSuccess: true; + currentUser: AuthenticatedUser; + } + | { + isSuccess: false; + response: IKibanaResponse; + }; export const performChecks = ({ - authenticatedUser, capability, context, - license, request, response, -}: PerformChecksParams): IKibanaResponse | undefined => { +}: PerformChecksParams): PerformChecks => { const assistantResponse = buildResponse(response); - if (license) { - if (!hasAIAssistantLicense(context.licensing.license)) { - return response.forbidden({ + if (!hasAIAssistantLicense(context.licensing.license)) { + return { + isSuccess: false, + response: response.forbidden({ body: { message: UPGRADE_LICENSE_MESSAGE, }, - }); - } + }), + }; } - if (authenticatedUser) { - if (context.elasticAssistant.getCurrentUser() == null) { - return assistantResponse.error({ + const currentUser = context.elasticAssistant.getCurrentUser(); + + if (currentUser == null) { + return { + isSuccess: false, + response: assistantResponse.error({ body: `Authenticated user not found`, statusCode: 401, - }); - } + }), + }; } if (capability) { @@ -619,11 +629,17 @@ export const performChecks = ({ }); const registeredFeatures = context.elasticAssistant.getRegisteredFeatures(pluginName); if (!registeredFeatures[capability]) { - return response.notFound(); + return { + isSuccess: false, + response: response.notFound(), + }; } } - return undefined; + return { + isSuccess: true, + currentUser, + }; }; /** @@ -653,23 +669,20 @@ export const isV2KnowledgeBaseEnabled = ({ * Telemetry function to determine whether knowledge base has been installed * @param kbDataClient */ -export const getIsKnowledgeBaseEnabled = async ( +export const getIsKnowledgeBaseInstalled = async ( kbDataClient?: AIAssistantKnowledgeBaseDataClient | null -): Promise<{ - esqlExists: boolean; - isModelDeployed: boolean; -}> => { - let esqlExists = false; +): Promise => { + let securityLabsDocsExist = false; let isModelDeployed = false; if (kbDataClient != null) { try { isModelDeployed = await kbDataClient.isModelDeployed(); if (isModelDeployed) { - esqlExists = + securityLabsDocsExist = ( await kbDataClient.getKnowledgeBaseDocumentEntries({ - query: ESQL_DOCS_LOADED_QUERY, - required: true, + kbResource: SECURITY_LABS_RESOURCE, + query: SECURITY_LABS_LOADED_QUERY, }) ).length > 0; } @@ -678,8 +691,5 @@ export const getIsKnowledgeBaseEnabled = async ( } } - return { - esqlExists, - isModelDeployed, - }; + return isModelDeployed && securityLabsDocsExist; }; diff --git a/x-pack/plugins/elastic_assistant/server/routes/index.ts b/x-pack/plugins/elastic_assistant/server/routes/index.ts index a6d7a4298c2b7..928c3211faa9b 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/index.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/index.ts @@ -14,6 +14,7 @@ export { getAttackDiscoveryRoute } from './attack_discovery/get/get_attack_disco // Knowledge Base export { deleteKnowledgeBaseRoute } from './knowledge_base/delete_knowledge_base'; +export { getKnowledgeBaseIndicesRoute } from './knowledge_base/get_knowledge_base_indices'; export { getKnowledgeBaseStatusRoute } from './knowledge_base/get_knowledge_base_status'; export { postKnowledgeBaseRoute } from './knowledge_base/post_knowledge_base'; diff --git a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/constants.ts b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/constants.ts index 89970611df0e9..052b2cac57609 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/constants.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/constants.ts @@ -12,3 +12,6 @@ export const KNOWLEDGE_BASE_INGEST_PIPELINE = '.kibana-elastic-ai-assistant-kb-i export const ESQL_DOCS_LOADED_QUERY = 'You can chain processing commands, separated by a pipe character: `|`.'; export const SECURITY_LABS_RESOURCE = 'security_labs'; +export const USER_RESOURCE = 'user'; +// Query for determining if Security Labs docs have been loaded. Intended for use with Telemetry +export const SECURITY_LABS_LOADED_QUERY = 'What is Elastic Security Labs'; diff --git a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/bulk_actions_route.ts b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/bulk_actions_route.ts index cfb2303010756..fbe73525578b0 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/bulk_actions_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/bulk_actions_route.ts @@ -6,7 +6,7 @@ */ import moment from 'moment'; -import type { AuthenticatedUser, IKibanaResponse, KibanaResponseFactory } from '@kbn/core/server'; +import type { IKibanaResponse, KibanaResponseFactory } from '@kbn/core/server'; import { transformError } from '@kbn/securitysolution-es-utils'; import { @@ -143,15 +143,13 @@ export const bulkActionKnowledgeBaseEntriesRoute = (router: ElasticAssistantPlug // Perform license, authenticated user and FF checks const checkResponse = performChecks({ - authenticatedUser: true, capability: 'assistantKnowledgeBaseByDefault', context: ctx, - license: true, request, response, }); - if (checkResponse) { - return checkResponse; + if (!checkResponse.isSuccess) { + return checkResponse.response; } logger.debug( @@ -181,8 +179,7 @@ export const bulkActionKnowledgeBaseEntriesRoute = (router: ElasticAssistantPlug v2KnowledgeBaseEnabled: true, }); const spaceId = ctx.elasticAssistant.getSpaceId(); - // Authenticated user null check completed in `performChecks()` above - const authenticatedUser = ctx.elasticAssistant.getCurrentUser() as AuthenticatedUser; + const authenticatedUser = checkResponse.currentUser; const userFilter = getKBUserFilter(authenticatedUser); const manageGlobalKnowledgeBaseAIAssistant = kbDataClient?.options.manageGlobalKnowledgeBaseAIAssistant; diff --git a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/create_route.ts b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/create_route.ts index 96753bdd690bd..0bfe9de269f7c 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/create_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/create_route.ts @@ -47,15 +47,13 @@ export const createKnowledgeBaseEntryRoute = (router: ElasticAssistantPluginRout // Perform license, authenticated user and FF checks const checkResponse = performChecks({ - authenticatedUser: true, capability: 'assistantKnowledgeBaseByDefault', context: ctx, - license: true, request, response, }); - if (checkResponse) { - return checkResponse; + if (!checkResponse.isSuccess) { + return checkResponse.response; } // Check mappings and upgrade if necessary -- this route only supports v2 KB, so always `true` diff --git a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/find_route.ts b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/find_route.ts index 356d5d9150a67..13334d0d829b1 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/find_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/find_route.ts @@ -58,21 +58,19 @@ export const findKnowledgeBaseEntriesRoute = (router: ElasticAssistantPluginRout // Perform license, authenticated user and FF checks const checkResponse = performChecks({ - authenticatedUser: true, capability: 'assistantKnowledgeBaseByDefault', context: ctx, - license: true, request, response, }); - if (checkResponse) { - return checkResponse; + if (!checkResponse.isSuccess) { + return checkResponse.response; } const kbDataClient = await ctx.elasticAssistant.getAIAssistantKnowledgeBaseDataClient({ v2KnowledgeBaseEnabled: true, }); - const currentUser = ctx.elasticAssistant.getCurrentUser(); + const currentUser = checkResponse.currentUser; const userFilter = getKBUserFilter(currentUser); const systemFilter = ` AND (kb_resource:"user" OR type:"index")`; const additionalFilter = query.filter ? ` AND ${query.filter}` : ''; diff --git a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_indices.test.ts b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_indices.test.ts new file mode 100644 index 0000000000000..e7eaa75407248 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_indices.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 { getKnowledgeBaseIndicesRoute } from './get_knowledge_base_indices'; +import { serverMock } from '../../__mocks__/server'; +import { requestContextMock } from '../../__mocks__/request_context'; +import { getGetKnowledgeBaseIndicesRequest } from '../../__mocks__/request'; + +const mockFieldCaps = { + indices: [ + '.ds-logs-endpoint.alerts-default-2024.10.31-000001', + '.ds-metrics-endpoint.metadata-default-2024.10.31-000001', + '.internal.alerts-security.alerts-default-000001', + 'metrics-endpoint.metadata_current_default', + 'semantic-index-1', + 'semantic-index-2', + ], + fields: { + content: { + unmapped: { + type: 'unmapped', + metadata_field: false, + searchable: false, + aggregatable: false, + indices: [ + '.ds-logs-endpoint.alerts-default-2024.10.31-000001', + '.ds-metrics-endpoint.metadata-default-2024.10.31-000001', + '.internal.alerts-security.alerts-default-000001', + 'metrics-endpoint.metadata_current_default', + ], + }, + semantic_text: { + type: 'semantic_text', + metadata_field: false, + searchable: true, + aggregatable: false, + indices: ['semantic-index-1', 'semantic-index-2'], + }, + }, + }, +}; + +describe('Get Knowledge Base Status Route', () => { + let server: ReturnType; + + let { context } = requestContextMock.createTools(); + + beforeEach(() => { + server = serverMock.create(); + ({ context } = requestContextMock.createTools()); + context.core.elasticsearch.client.asCurrentUser.fieldCaps.mockResponse(mockFieldCaps); + + getKnowledgeBaseIndicesRoute(server.router); + }); + + describe('Status codes', () => { + test('returns 200 and all indices with `semantic_text` type fields', async () => { + const response = await server.inject( + getGetKnowledgeBaseIndicesRequest(), + requestContextMock.convertContext(context) + ); + + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + indices: ['semantic-index-1', 'semantic-index-2'], + }); + expect(context.core.elasticsearch.client.asCurrentUser.fieldCaps).toBeCalledWith({ + index: '*', + fields: '*', + types: ['semantic_text'], + include_unmapped: true, + }); + }); + }); +}); diff --git a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_indices.ts b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_indices.ts new file mode 100644 index 0000000000000..18191291468de --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_indices.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { transformError } from '@kbn/securitysolution-es-utils'; + +import { + ELASTIC_AI_ASSISTANT_INTERNAL_API_VERSION, + ELASTIC_AI_ASSISTANT_KNOWLEDGE_BASE_INDICES_URL, + GetKnowledgeBaseIndicesResponse, +} from '@kbn/elastic-assistant-common'; +import { IKibanaResponse } from '@kbn/core/server'; +import { buildResponse } from '../../lib/build_response'; +import { ElasticAssistantPluginRouter } from '../../types'; + +/** + * Get the indices that have fields of `sematic_text` type + * + * @param router IRouter for registering routes + */ +export const getKnowledgeBaseIndicesRoute = (router: ElasticAssistantPluginRouter) => { + router.versioned + .get({ + access: 'internal', + path: ELASTIC_AI_ASSISTANT_KNOWLEDGE_BASE_INDICES_URL, + options: { + tags: ['access:elasticAssistant'], + }, + }) + .addVersion( + { + version: ELASTIC_AI_ASSISTANT_INTERNAL_API_VERSION, + validate: false, + }, + async (context, _, response): Promise> => { + const resp = buildResponse(response); + const ctx = await context.resolve(['core', 'elasticAssistant', 'licensing']); + const logger = ctx.elasticAssistant.logger; + const esClient = ctx.core.elasticsearch.client.asCurrentUser; + + try { + const body: GetKnowledgeBaseIndicesResponse = { + indices: [], + }; + + const res = await esClient.fieldCaps({ + index: '*', + fields: '*', + types: ['semantic_text'], + include_unmapped: true, + }); + + const indices = res.fields.content?.semantic_text?.indices; + if (indices) { + body.indices = Array.isArray(indices) ? indices : [indices]; + } + + return response.ok({ body }); + } catch (err) { + logger.error(err); + const error = transformError(err); + + return resp.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_status.test.ts b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_status.test.ts index 6244599a2af27..b30e5ac3653ad 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_status.test.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_status.test.ts @@ -38,6 +38,7 @@ describe('Get Knowledge Base Status Route', () => { isModelDeployed: jest.fn().mockResolvedValue(true), isSetupInProgress: false, isSecurityLabsDocsLoaded: jest.fn().mockResolvedValue(true), + isUserDataExists: jest.fn().mockResolvedValue(true), }); getKnowledgeBaseStatusRoute(server.router); @@ -58,6 +59,7 @@ describe('Get Knowledge Base Status Route', () => { is_setup_available: true, pipeline_exists: true, security_labs_exists: true, + user_data_exists: true, }); }); }); diff --git a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_status.ts b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_status.ts index 833e674b68ffd..f278cd469ac0e 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_status.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_status.ts @@ -74,11 +74,18 @@ export const getKnowledgeBaseStatusRoute = (router: ElasticAssistantPluginRouter }; if (indexExists && isModelDeployed) { - const securityLabsExists = await kbDataClient.isSecurityLabsDocsLoaded(); + const securityLabsExists = v2KnowledgeBaseEnabled + ? await kbDataClient.isSecurityLabsDocsLoaded() + : true; + const userDataExists = v2KnowledgeBaseEnabled + ? await kbDataClient.isUserDataExists() + : true; + return response.ok({ body: { ...body, - security_labs_exists: v2KnowledgeBaseEnabled ? securityLabsExists : true, + security_labs_exists: securityLabsExists, + user_data_exists: userDataExists, }, }); } diff --git a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/post_knowledge_base.ts b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/post_knowledge_base.ts index 96317da303ac1..23604886e4a52 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/post_knowledge_base.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/post_knowledge_base.ts @@ -60,6 +60,7 @@ export const postKnowledgeBaseRoute = (router: ElasticAssistantPluginRouter) => // Only allow modelId override if FF is enabled as this will re-write the ingest pipeline and break any previous KB entries // This is only really needed for API integration tests const modelIdOverride = v2KnowledgeBaseEnabled ? request.query.modelId : undefined; + const ignoreSecurityLabs = request.query.ignoreSecurityLabs; try { const knowledgeBaseDataClient = @@ -74,6 +75,7 @@ export const postKnowledgeBaseRoute = (router: ElasticAssistantPluginRouter) => await knowledgeBaseDataClient.setupKnowledgeBase({ soClient, v2KnowledgeBaseEnabled, + ignoreSecurityLabs, }); return response.ok({ body: { success: true } }); diff --git a/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.test.ts b/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.test.ts index d19127be0d7e8..a7abac27dac6f 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.test.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.test.ts @@ -46,7 +46,18 @@ jest.mock('../lib/executor', () => ({ const mockStream = jest.fn().mockImplementation(() => new PassThrough()); const mockLangChainExecute = langChainExecute as jest.Mock; const mockAppendAssistantMessageToConversation = appendAssistantMessageToConversation as jest.Mock; -jest.mock('./helpers'); +jest.mock('./helpers', () => { + const original = jest.requireActual('./helpers'); + + return { + ...original, + getIsKnowledgeBaseInstalled: jest.fn(), + appendAssistantMessageToConversation: jest.fn(), + langChainExecute: jest.fn(), + getPluginNameFromRequest: jest.fn(), + getSystemPromptFromUserConversation: jest.fn(), + }; +}); const existingConversation = getConversationResponseMock(); const reportEvent = jest.fn(); const appendConversationMessages = jest.fn(); diff --git a/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts b/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts index 4b65b5bb3f1e5..bb217f7f5aa3a 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts @@ -24,10 +24,11 @@ import { ElasticAssistantRequestHandlerContext, GetElser } from '../types'; import { appendAssistantMessageToConversation, DEFAULT_PLUGIN_NAME, - getIsKnowledgeBaseEnabled, + getIsKnowledgeBaseInstalled, getPluginNameFromRequest, getSystemPromptFromUserConversation, langChainExecute, + performChecks, } from './helpers'; import { isOpenSourceModel } from './utils'; @@ -66,12 +67,16 @@ export const postActionsConnectorExecuteRoute = ( let onLlmResponse; try { - const authenticatedUser = assistantContext.getCurrentUser(); - if (authenticatedUser == null) { - return response.unauthorized({ - body: `Authenticated user not found`, - }); + const checkResponse = performChecks({ + context: ctx, + request, + response, + }); + + if (!checkResponse.isSuccess) { + return checkResponse.response; } + let latestReplacements: Replacements = request.body.replacements; const onNewReplacements = (newReplacements: Replacements) => { latestReplacements = { ...latestReplacements, ...newReplacements }; @@ -165,14 +170,13 @@ export const postActionsConnectorExecuteRoute = ( (await assistantContext.getAIAssistantKnowledgeBaseDataClient({ v2KnowledgeBaseEnabled, })) ?? undefined; - const isEnabledKnowledgeBase = await getIsKnowledgeBaseEnabled(kbDataClient); - + const isKnowledgeBaseInstalled = await getIsKnowledgeBaseInstalled(kbDataClient); telemetry.reportEvent(INVOKE_ASSISTANT_ERROR_EVENT.eventType, { actionTypeId: request.body.actionTypeId, model: request.body.model, errorMessage: error.message, assistantStreamingEnabled: request.body.subAction !== 'invokeAI', - isEnabledKnowledgeBase, + isEnabledKnowledgeBase: isKnowledgeBaseInstalled, }); return resp.error({ diff --git a/x-pack/plugins/elastic_assistant/server/routes/prompts/bulk_actions_route.ts b/x-pack/plugins/elastic_assistant/server/routes/prompts/bulk_actions_route.ts index 44a949cd22eeb..d3ee47854e7a0 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/prompts/bulk_actions_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/prompts/bulk_actions_route.ts @@ -35,7 +35,7 @@ import { transformESSearchToPrompts, } from '../../ai_assistant_data_clients/prompts/helpers'; import { EsPromptsSchema, UpdatePromptSchema } from '../../ai_assistant_data_clients/prompts/types'; -import { UPGRADE_LICENSE_MESSAGE, hasAIAssistantLicense } from '../helpers'; +import { performChecks } from '../helpers'; export interface BulkOperationError { message: string; @@ -156,22 +156,17 @@ export const bulkPromptsRoute = (router: ElasticAssistantPluginRouter, logger: L request.events.completed$.subscribe(() => abortController.abort()); try { const ctx = await context.resolve(['core', 'elasticAssistant', 'licensing']); - const license = ctx.licensing.license; - if (!hasAIAssistantLicense(license)) { - return response.forbidden({ - body: { - message: UPGRADE_LICENSE_MESSAGE, - }, - }); + // Perform license and authenticated user checks + const checkResponse = performChecks({ + context: ctx, + request, + response, + }); + if (!checkResponse.isSuccess) { + return checkResponse.response; } + const authenticatedUser = checkResponse.currentUser; - const authenticatedUser = ctx.elasticAssistant.getCurrentUser(); - if (authenticatedUser == null) { - return assistantResponse.error({ - body: `Authenticated user not found`, - statusCode: 401, - }); - } const dataClient = await ctx.elasticAssistant.getAIAssistantPromptsDataClient(); if (body.create && body.create.length > 0) { @@ -211,7 +206,7 @@ export const bulkPromptsRoute = (router: ElasticAssistantPluginRouter, logger: L ), getUpdateScript: (document: UpdatePromptSchema) => getUpdateScript({ prompt: document, isPatch: true }), - authenticatedUser, + authenticatedUser: authenticatedUser ?? undefined, }); const created = docsCreated.length > 0 diff --git a/x-pack/plugins/elastic_assistant/server/routes/prompts/find_route.test.ts b/x-pack/plugins/elastic_assistant/server/routes/prompts/find_route.test.ts index 68ce67d842a0f..151c1622d0219 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/prompts/find_route.test.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/prompts/find_route.test.ts @@ -12,6 +12,7 @@ import { requestContextMock } from '../../__mocks__/request_context'; import { getFindPromptsResultWithSingleHit } from '../../__mocks__/response'; import { findPromptsRoute } from './find_route'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; +import type { AuthenticatedUser } from '@kbn/core-security-common'; describe('Find user prompts route', () => { let server: ReturnType; @@ -21,19 +22,26 @@ describe('Find user prompts route', () => { beforeEach(async () => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); + const mockUser1 = { + username: 'my_username', + authentication_realm: { + type: 'my_realm_type', + name: 'my_realm_name', + }, + } as AuthenticatedUser; clients.elasticAssistant.getAIAssistantPromptsDataClient.findDocuments.mockResolvedValue( Promise.resolve(getFindPromptsResultWithSingleHit()) ); - clients.elasticAssistant.getCurrentUser.mockResolvedValue({ + context.elasticAssistant.getCurrentUser.mockReturnValue({ username: 'my_username', authentication_realm: { type: 'my_realm_type', name: 'my_realm_name', }, - }); + } as AuthenticatedUser); logger = loggingSystemMock.createLogger(); - + context.elasticAssistant.getCurrentUser.mockReturnValue(mockUser1); findPromptsRoute(server.router, logger); }); diff --git a/x-pack/plugins/elastic_assistant/server/routes/prompts/find_route.ts b/x-pack/plugins/elastic_assistant/server/routes/prompts/find_route.ts index 848680be662a3..a2980b173d76a 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/prompts/find_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/prompts/find_route.ts @@ -18,7 +18,7 @@ import { ElasticAssistantPluginRouter } from '../../types'; import { buildResponse } from '../utils'; import { EsPromptsSchema } from '../../ai_assistant_data_clients/prompts/types'; import { transformESSearchToPrompts } from '../../ai_assistant_data_clients/prompts/helpers'; -import { UPGRADE_LICENSE_MESSAGE, hasAIAssistantLicense } from '../helpers'; +import { performChecks } from '../helpers'; export const findPromptsRoute = (router: ElasticAssistantPluginRouter, logger: Logger) => { router.versioned @@ -44,13 +44,14 @@ export const findPromptsRoute = (router: ElasticAssistantPluginRouter, logger: L try { const { query } = request; const ctx = await context.resolve(['core', 'elasticAssistant', 'licensing']); - const license = ctx.licensing.license; - if (!hasAIAssistantLicense(license)) { - return response.forbidden({ - body: { - message: UPGRADE_LICENSE_MESSAGE, - }, - }); + // Perform license and authenticated user checks + const checkResponse = performChecks({ + context: ctx, + request, + response, + }); + if (!checkResponse.isSuccess) { + return checkResponse.response; } const dataClient = await ctx.elasticAssistant.getAIAssistantPromptsDataClient(); diff --git a/x-pack/plugins/elastic_assistant/server/routes/register_routes.ts b/x-pack/plugins/elastic_assistant/server/routes/register_routes.ts index 7898629e15b5c..d722e31cb2338 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/register_routes.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/register_routes.ts @@ -18,6 +18,7 @@ import { updateConversationRoute } from './user_conversations/update_route'; import { findUserConversationsRoute } from './user_conversations/find_route'; import { bulkActionConversationsRoute } from './user_conversations/bulk_actions_route'; import { appendConversationMessageRoute } from './user_conversations/append_conversation_messages_route'; +import { getKnowledgeBaseIndicesRoute } from './knowledge_base/get_knowledge_base_indices'; import { getKnowledgeBaseStatusRoute } from './knowledge_base/get_knowledge_base_status'; import { postKnowledgeBaseRoute } from './knowledge_base/post_knowledge_base'; import { getEvaluateRoute } from './evaluate/get_evaluate'; @@ -60,6 +61,7 @@ export const registerRoutes = ( findUserConversationsRoute(router); // Knowledge Base Setup + getKnowledgeBaseIndicesRoute(router); getKnowledgeBaseStatusRoute(router); postKnowledgeBaseRoute(router); diff --git a/x-pack/plugins/elastic_assistant/server/routes/request_context_factory.ts b/x-pack/plugins/elastic_assistant/server/routes/request_context_factory.ts index eeb1a5564d1cf..7d97029e7252a 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/request_context_factory.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/request_context_factory.ts @@ -101,6 +101,7 @@ export class RequestContextFactory implements IRequestContextFactory { return this.assistantService.createAIAssistantKnowledgeBaseDataClient({ spaceId: getSpaceId(), logger: this.logger, + licensing: context.licensing, currentUser, modelIdOverride, v2KnowledgeBaseEnabled, @@ -114,6 +115,7 @@ export class RequestContextFactory implements IRequestContextFactory { const currentUser = getCurrentUser(); return this.assistantService.createAttackDiscoveryDataClient({ spaceId: getSpaceId(), + licensing: context.licensing, logger: this.logger, currentUser, }); @@ -123,6 +125,7 @@ export class RequestContextFactory implements IRequestContextFactory { const currentUser = getCurrentUser(); return this.assistantService.createAIAssistantPromptsDataClient({ spaceId: getSpaceId(), + licensing: context.licensing, logger: this.logger, currentUser, }); @@ -132,6 +135,7 @@ export class RequestContextFactory implements IRequestContextFactory { const currentUser = getCurrentUser(); return this.assistantService.createAIAssistantAnonymizationFieldsDataClient({ spaceId: getSpaceId(), + licensing: context.licensing, logger: this.logger, currentUser, }); @@ -141,6 +145,7 @@ export class RequestContextFactory implements IRequestContextFactory { const currentUser = getCurrentUser(); return this.assistantService.createAIAssistantConversationsDataClient({ spaceId: getSpaceId(), + licensing: context.licensing, logger: this.logger, currentUser, }); diff --git a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/append_conversation_messages_route.ts b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/append_conversation_messages_route.ts index 796c0d617fe5d..06bfa023136d9 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/append_conversation_messages_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/append_conversation_messages_route.ts @@ -17,7 +17,7 @@ import { import { buildRouteValidationWithZod } from '@kbn/elastic-assistant-common/impl/schemas/common'; import { buildResponse } from '../utils'; import { ElasticAssistantPluginRouter } from '../../types'; -import { UPGRADE_LICENSE_MESSAGE, hasAIAssistantLicense } from '../helpers'; +import { performChecks } from '../helpers'; export const appendConversationMessageRoute = (router: ElasticAssistantPluginRouter) => { router.versioned @@ -43,22 +43,16 @@ export const appendConversationMessageRoute = (router: ElasticAssistantPluginRou const { id } = request.params; try { const ctx = await context.resolve(['core', 'elasticAssistant', 'licensing']); - const license = ctx.licensing.license; - if (!hasAIAssistantLicense(license)) { - return response.forbidden({ - body: { - message: UPGRADE_LICENSE_MESSAGE, - }, - }); + const checkResponse = performChecks({ + context: ctx, + request, + response, + }); + if (!checkResponse.isSuccess) { + return checkResponse.response; } const dataClient = await ctx.elasticAssistant.getAIAssistantConversationsDataClient(); - const authenticatedUser = ctx.elasticAssistant.getCurrentUser(); - if (authenticatedUser == null) { - return assistantResponse.error({ - body: `Authenticated user not found`, - statusCode: 401, - }); - } + const authenticatedUser = checkResponse.currentUser; const existingConversation = await dataClient?.getConversation({ id, authenticatedUser }); if (existingConversation == null) { diff --git a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/bulk_actions_route.ts b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/bulk_actions_route.ts index 6e30acb1a47c7..9c353997f1d46 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/bulk_actions_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/bulk_actions_route.ts @@ -35,7 +35,7 @@ import { transformToUpdateScheme, } from '../../ai_assistant_data_clients/conversations/update_conversation'; import { EsConversationSchema } from '../../ai_assistant_data_clients/conversations/types'; -import { UPGRADE_LICENSE_MESSAGE, hasAIAssistantLicense } from '../helpers'; +import { performChecks } from '../helpers'; export interface BulkOperationError { message: string; @@ -156,23 +156,17 @@ export const bulkActionConversationsRoute = ( request.events.completed$.subscribe(() => abortController.abort()); try { const ctx = await context.resolve(['core', 'elasticAssistant', 'licensing']); - const license = ctx.licensing.license; - if (!hasAIAssistantLicense(license)) { - return response.forbidden({ - body: { - message: UPGRADE_LICENSE_MESSAGE, - }, - }); + const checkResponse = performChecks({ + context: ctx, + request, + response, + }); + if (!checkResponse.isSuccess) { + return checkResponse.response; } + const authenticatedUser = checkResponse.currentUser; const dataClient = await ctx.elasticAssistant.getAIAssistantConversationsDataClient(); const spaceId = ctx.elasticAssistant.getSpaceId(); - const authenticatedUser = ctx.elasticAssistant.getCurrentUser(); - if (authenticatedUser == null) { - return assistantResponse.error({ - body: `Authenticated user not found`, - statusCode: 401, - }); - } if (body.create && body.create.length > 0) { const userFilter = authenticatedUser?.username diff --git a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/create_route.ts b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/create_route.ts index b92ad5462963e..9955494b5f294 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/create_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/create_route.ts @@ -44,14 +44,12 @@ export const createConversationRoute = (router: ElasticAssistantPluginRouter): v const ctx = await context.resolve(['core', 'elasticAssistant', 'licensing']); // Perform license and authenticated user checks const checkResponse = performChecks({ - authenticatedUser: true, context: ctx, - license: true, request, response, }); - if (checkResponse) { - return checkResponse; + if (!checkResponse.isSuccess) { + return checkResponse.response; } const dataClient = await ctx.elasticAssistant.getAIAssistantConversationsDataClient(); diff --git a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/delete_route.ts b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/delete_route.ts index 5d761c09f682c..9c974fdb78de8 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/delete_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/delete_route.ts @@ -14,7 +14,7 @@ import { import { buildRouteValidationWithZod } from '@kbn/elastic-assistant-common/impl/schemas/common'; import { ElasticAssistantPluginRouter } from '../../types'; import { buildResponse } from '../utils'; -import { UPGRADE_LICENSE_MESSAGE, hasAIAssistantLicense } from '../helpers'; +import { performChecks } from '../helpers'; export const deleteConversationRoute = (router: ElasticAssistantPluginRouter) => { router.versioned @@ -40,23 +40,18 @@ export const deleteConversationRoute = (router: ElasticAssistantPluginRouter) => const { id } = request.params; const ctx = await context.resolve(['core', 'elasticAssistant', 'licensing']); - const license = ctx.licensing.license; - if (!hasAIAssistantLicense(license)) { - return response.forbidden({ - body: { - message: UPGRADE_LICENSE_MESSAGE, - }, - }); + const checkResponse = performChecks({ + context: ctx, + request, + response, + }); + if (!checkResponse.isSuccess) { + return checkResponse.response; } const dataClient = await ctx.elasticAssistant.getAIAssistantConversationsDataClient(); - const authenticatedUser = ctx.elasticAssistant.getCurrentUser(); - if (authenticatedUser == null) { - return assistantResponse.error({ - body: `Authenticated user not found`, - statusCode: 401, - }); - } + const authenticatedUser = checkResponse.currentUser; + const existingConversation = await dataClient?.getConversation({ id, authenticatedUser }); if (existingConversation == null) { return assistantResponse.error({ diff --git a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/find_route.test.ts b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/find_route.test.ts index 63141fe5475a6..2b20ab03371f6 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/find_route.test.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/find_route.test.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import { type AuthenticatedUser } from '@kbn/core/server'; import { getCurrentUserFindRequest, requestMock } from '../../__mocks__/request'; import { ELASTIC_AI_ASSISTANT_CONVERSATIONS_URL_FIND } from '@kbn/elastic-assistant-common'; import { serverMock } from '../../__mocks__/server'; @@ -15,7 +15,6 @@ import { findUserConversationsRoute } from './find_route'; describe('Find user conversations route', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); - beforeEach(async () => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); @@ -23,13 +22,13 @@ describe('Find user conversations route', () => { clients.elasticAssistant.getAIAssistantConversationsDataClient.findDocuments.mockResolvedValue( Promise.resolve(getFindConversationsResultWithSingleHit()) ); - clients.elasticAssistant.getCurrentUser.mockResolvedValue({ + context.elasticAssistant.getCurrentUser.mockReturnValue({ username: 'my_username', authentication_realm: { type: 'my_realm_type', name: 'my_realm_name', }, - }); + } as AuthenticatedUser); findUserConversationsRoute(server.router); }); diff --git a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/find_route.ts b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/find_route.ts index e7ce80039beb0..07ba23710b12c 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/find_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/find_route.ts @@ -21,7 +21,7 @@ import { ElasticAssistantPluginRouter } from '../../types'; import { buildResponse } from '../utils'; import { EsConversationSchema } from '../../ai_assistant_data_clients/conversations/types'; import { transformESSearchToConversations } from '../../ai_assistant_data_clients/conversations/transforms'; -import { UPGRADE_LICENSE_MESSAGE, hasAIAssistantLicense } from '../helpers'; +import { performChecks } from '../helpers'; export const findUserConversationsRoute = (router: ElasticAssistantPluginRouter) => { router.versioned @@ -46,16 +46,17 @@ export const findUserConversationsRoute = (router: ElasticAssistantPluginRouter) try { const { query } = request; const ctx = await context.resolve(['core', 'elasticAssistant', 'licensing']); - const license = ctx.licensing.license; - if (!hasAIAssistantLicense(license)) { - return response.forbidden({ - body: { - message: UPGRADE_LICENSE_MESSAGE, - }, - }); + // Perform license and authenticated user checks + const checkResponse = performChecks({ + context: ctx, + request, + response, + }); + if (!checkResponse.isSuccess) { + return checkResponse.response; } const dataClient = await ctx.elasticAssistant.getAIAssistantConversationsDataClient(); - const currentUser = ctx.elasticAssistant.getCurrentUser(); + const currentUser = checkResponse.currentUser; const additionalFilter = query.filter ? ` AND ${query.filter}` : ''; const userFilter = currentUser?.username diff --git a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/read_route.ts b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/read_route.ts index dd540897b0ece..ab69dc20999a2 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/read_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/read_route.ts @@ -16,7 +16,7 @@ import { ReadConversationRequestParams } from '@kbn/elastic-assistant-common/imp import { buildRouteValidationWithZod } from '@kbn/elastic-assistant-common/impl/schemas/common'; import { buildResponse } from '../utils'; import { ElasticAssistantPluginRouter } from '../../types'; -import { UPGRADE_LICENSE_MESSAGE, hasAIAssistantLicense } from '../helpers'; +import { performChecks } from '../helpers'; export const readConversationRoute = (router: ElasticAssistantPluginRouter) => { router.versioned @@ -43,21 +43,15 @@ export const readConversationRoute = (router: ElasticAssistantPluginRouter) => { try { const ctx = await context.resolve(['core', 'elasticAssistant', 'licensing']); - const license = ctx.licensing.license; - if (!hasAIAssistantLicense(license)) { - return response.forbidden({ - body: { - message: UPGRADE_LICENSE_MESSAGE, - }, - }); - } - const authenticatedUser = ctx.elasticAssistant.getCurrentUser(); - if (authenticatedUser == null) { - return assistantResponse.error({ - body: `Authenticated user not found`, - statusCode: 401, - }); + const checkResponse = performChecks({ + context: ctx, + request, + response, + }); + if (!checkResponse.isSuccess) { + return checkResponse.response; } + const authenticatedUser = checkResponse.currentUser; const dataClient = await ctx.elasticAssistant.getAIAssistantConversationsDataClient(); const conversation = await dataClient?.getConversation({ id, authenticatedUser }); diff --git a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/update_route.ts b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/update_route.ts index 4ad819ef0caa0..41956b9bc80f7 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/update_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/update_route.ts @@ -45,18 +45,16 @@ export const updateConversationRoute = (router: ElasticAssistantPluginRouter) => const { id } = request.params; try { const ctx = await context.resolve(['core', 'elasticAssistant', 'licensing']); - const authenticatedUser = ctx.elasticAssistant.getCurrentUser(); // Perform license and authenticated user checks const checkResponse = performChecks({ - authenticatedUser: true, context: ctx, - license: true, request, response, }); - if (checkResponse) { - return checkResponse; + if (!checkResponse.isSuccess) { + return checkResponse.response; } + const authenticatedUser = checkResponse.currentUser; const dataClient = await ctx.elasticAssistant.getAIAssistantConversationsDataClient(); diff --git a/x-pack/plugins/elastic_assistant/tsconfig.json b/x-pack/plugins/elastic_assistant/tsconfig.json index 747a58ed930d3..d3436f28a1d3e 100644 --- a/x-pack/plugins/elastic_assistant/tsconfig.json +++ b/x-pack/plugins/elastic_assistant/tsconfig.json @@ -48,7 +48,8 @@ "@kbn/apm-utils", "@kbn/std", "@kbn/zod", - "@kbn/inference-plugin" + "@kbn/inference-plugin", + "@kbn/data-views-plugin" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_object_type_definition.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_object_type_definition.ts index d8ce2daa6efbe..bb07842e2bab5 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_object_type_definition.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_object_type_definition.ts @@ -16,6 +16,7 @@ export class EncryptedSavedObjectAttributesDefinition { public readonly attributesToEncrypt: ReadonlySet; private readonly attributesToIncludeInAAD: ReadonlySet | undefined; private readonly attributesToStrip: ReadonlySet; + public readonly enforceRandomId: boolean; constructor(typeRegistration: EncryptedSavedObjectTypeRegistration) { if (typeRegistration.attributesToIncludeInAAD) { @@ -49,6 +50,8 @@ export class EncryptedSavedObjectAttributesDefinition { } } + this.enforceRandomId = typeRegistration.enforceRandomId !== false; + this.attributesToEncrypt = attributesToEncrypt; this.attributesToStrip = attributesToStrip; this.attributesToIncludeInAAD = typeRegistration.attributesToIncludeInAAD; diff --git a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.test.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.test.ts index 1691d3f4c0610..67c972ec5f859 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.test.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.test.ts @@ -2405,3 +2405,24 @@ describe('#decryptAttributesSync', () => { }); }); }); + +describe('#shouldEnforceRandomId', () => { + it('defaults to true if enforceRandomId is undefined', () => { + service.registerType({ type: 'known-type-1', attributesToEncrypt: new Set(['attr']) }); + expect(service.shouldEnforceRandomId('known-type-1')).toBe(true); + }); + it('should return the value of enforceRandomId if it is defined', () => { + service.registerType({ + type: 'known-type-1', + attributesToEncrypt: new Set(['attr']), + enforceRandomId: false, + }); + service.registerType({ + type: 'known-type-2', + attributesToEncrypt: new Set(['attr']), + enforceRandomId: true, + }); + expect(service.shouldEnforceRandomId('known-type-1')).toBe(false); + expect(service.shouldEnforceRandomId('known-type-2')).toBe(true); + }); +}); 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 44072a0828d48..d2c7d9975a9ca 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 @@ -33,6 +33,7 @@ export interface EncryptedSavedObjectTypeRegistration { readonly type: string; readonly attributesToEncrypt: ReadonlySet; readonly attributesToIncludeInAAD?: ReadonlySet; + readonly enforceRandomId?: boolean; } /** @@ -152,6 +153,16 @@ export class EncryptedSavedObjectsService { return this.typeDefinitions.has(type); } + /** + * Checks whether the ESO type has explicitly opted out of enforcing random IDs. + * @param type Saved object type. + * @returns boolean - true unless explicitly opted out by setting enforceRandomId to false + */ + public shouldEnforceRandomId(type: string) { + const typeDefinition = this.typeDefinitions.get(type); + return typeDefinition?.enforceRandomId !== false; + } + /** * Takes saved object attributes for the specified type and, depending on the type definition, * either decrypts or strips encrypted attributes (e.g. in case AAD or encryption key has changed diff --git a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/saved_objects_encryption_extension.ts b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/saved_objects_encryption_extension.ts index 01c35c7403fdf..45e0f6a46c892 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/saved_objects_encryption_extension.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/saved_objects_encryption_extension.ts @@ -40,6 +40,10 @@ export class SavedObjectsEncryptionExtension implements ISavedObjectsEncryptionE return this._service.isRegistered(type); } + shouldEnforceRandomId(type: string) { + return this._service.shouldEnforceRandomId(type); + } + async decryptOrStripResponseAttributes>( response: R, originalAttributes?: T diff --git a/x-pack/plugins/enterprise_search/common/constants.ts b/x-pack/plugins/enterprise_search/common/constants.ts index 4da0244b2ec5e..797f94fa29e51 100644 --- a/x-pack/plugins/enterprise_search/common/constants.ts +++ b/x-pack/plugins/enterprise_search/common/constants.ts @@ -15,6 +15,10 @@ import { ENTERPRISE_SEARCH_ANALYTICS_APP_ID, ENTERPRISE_SEARCH_APPSEARCH_APP_ID, ENTERPRISE_SEARCH_WORKPLACESEARCH_APP_ID, + SEARCH_ELASTICSEARCH, + SEARCH_VECTOR_SEARCH, + SEARCH_SEMANTIC_SEARCH, + SEARCH_AI_SEARCH, } from '@kbn/deeplinks-search'; import { i18n } from '@kbn/i18n'; @@ -58,7 +62,7 @@ export const ENTERPRISE_SEARCH_CONTENT_PLUGIN = { }; export const AI_SEARCH_PLUGIN = { - ID: 'enterpriseSearchAISearch', + ID: SEARCH_AI_SEARCH, NAME: i18n.translate('xpack.enterpriseSearch.aiSearch.productName', { defaultMessage: 'AI Search', }), @@ -91,7 +95,7 @@ export const ANALYTICS_PLUGIN = { }; export const ELASTICSEARCH_PLUGIN = { - ID: 'enterpriseSearchElasticsearch', + ID: SEARCH_ELASTICSEARCH, NAME: i18n.translate('xpack.enterpriseSearch.elasticsearch.productName', { defaultMessage: 'Elasticsearch', }), @@ -167,7 +171,7 @@ export const VECTOR_SEARCH_PLUGIN = { defaultMessage: 'Elasticsearch can be used as a vector database, which enables vector search and semantic search use cases.', }), - ID: 'enterpriseSearchVectorSearch', + ID: SEARCH_VECTOR_SEARCH, LOGO: 'logoEnterpriseSearch', NAME: i18n.translate('xpack.enterpriseSearch.vectorSearch.productName', { defaultMessage: 'Vector Search', @@ -184,7 +188,7 @@ export const SEMANTIC_SEARCH_PLUGIN = { defaultMessage: 'Easily add semantic search to Elasticsearch with inference endpoints and the semantic_text field type, to boost search relevance.', }), - ID: 'enterpriseSearchSemanticSearch', + ID: SEARCH_SEMANTIC_SEARCH, LOGO: 'logoEnterpriseSearch', NAME: i18n.translate('xpack.enterpriseSearch.SemanticSearch.productName', { defaultMessage: 'Semantic Search', @@ -218,50 +222,6 @@ export const CREATE_CONNECTOR_PLUGIN = { --index-language en --from-file config.yml `, - CONSOLE_SNIPPET: dedent`# Create an index -PUT /my-index-000001 -{ - "settings": { - "index": { - "number_of_shards": 3, - "number_of_replicas": 2 - } - } -} - -# Create an API key -POST /_security/api_key -{ - "name": "my-api-key", - "expiration": "1d", - "role_descriptors": - { - "role-a": { - "cluster": ["all"], - "indices": [ - { - "names": ["index-a*"], - "privileges": ["read"] - } - ] - }, - "role-b": { - "cluster": ["all"], - "indices": [ - { - "names": ["index-b*"], - "privileges": ["all"] - }] - } - }, "metadata": - { "application": "my-application", - "environment": { - "level": 1, - "trusted": true, - "tags": ["dev", "staging"] - } - } - }`, }; export const LICENSED_SUPPORT_URL = 'https://support.elastic.co'; @@ -341,3 +301,14 @@ export const CRAWLER = { // TODO remove this once the connector service types are no longer in "example" state export const EXAMPLE_CONNECTOR_SERVICE_TYPES = ['opentext_documentum']; + +export const GETTING_STARTED_TITLE = i18n.translate('xpack.enterpriseSearch.gettingStarted.title', { + defaultMessage: 'Getting started', +}); + +export const SEARCH_APPS_BREADCRUMB = i18n.translate( + 'xpack.enterpriseSearch.searchApplications.breadcrumb', + { + defaultMessage: 'Search Applications', + } +); diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts index cca5523ded681..9b37c661d923a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts @@ -41,6 +41,7 @@ export const mockKibanaValues = { data: dataPluginMock.createStartContract(), esConfig: { elasticsearch_host: 'https://your_deployment_url' }, getChromeStyle$: jest.fn().mockReturnValue(of('classic')), + getNavLinks: jest.fn().mockReturnValue([]), guidedOnboarding: {}, history: mockHistory, indexMappingComponent: null, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/api_logs/components/empty_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/api_logs/components/empty_state.tsx index c78bf3e918737..bf8cf009759d0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/api_logs/components/empty_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/api_logs/components/empty_state.tsx @@ -11,7 +11,7 @@ import { EuiButton, EuiEmptyPrompt } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { API_DOCS_URL } from '../../../routes'; +import { docLinks } from '../../../../shared/doc_links'; export const EmptyState: React.FC = () => ( (

} actions={ - + {i18n.translate('xpack.enterpriseSearch.appSearch.engine.apiLogs.empty.buttonLabel', { defaultMessage: 'View the API reference', })} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_rules_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_rules_table.tsx index bef8ed4462fdc..2f66dc455442e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_rules_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_rules_table.tsx @@ -23,11 +23,11 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { docLinks } from '../../../../shared/doc_links'; import { clearFlashMessages, flashSuccessToast } from '../../../../shared/flash_messages'; import { GenericEndpointInlineEditableTable } from '../../../../shared/tables/generic_endpoint_inline_editable_table'; import { InlineEditableTableColumn } from '../../../../shared/tables/inline_editable_table/types'; import { ItemWithAnID } from '../../../../shared/tables/types'; -import { CRAWL_RULES_DOCS_URL } from '../../../routes'; import { CrawlerSingleDomainLogic } from '../crawler_single_domain_logic'; import { CrawlerPolicies, @@ -53,7 +53,7 @@ const DEFAULT_DESCRIPTION = ( defaultMessage="Create a crawl rule to include or exclude pages whose URL matches the rule. Rules run in sequential order, and each URL is evaluated according to the first match. {link}" values={{ link: ( - + {i18n.translate( 'xpack.enterpriseSearch.appSearch.crawler.crawlRulesTable.descriptionLinkText', { defaultMessage: 'Learn more about crawl rules' } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/deduplication_panel/deduplication_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/deduplication_panel/deduplication_panel.tsx index 26794d0421353..ef4d7448a5785 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/deduplication_panel/deduplication_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/deduplication_panel/deduplication_panel.tsx @@ -27,7 +27,7 @@ import { EuiSelectableLIOption } from '@elastic/eui/src/components/selectable/se import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { DUPLICATE_DOCS_URL } from '../../../../routes'; +import { docLinks } from '../../../../../shared/doc_links'; import { DataPanel } from '../../../data_panel'; import { CrawlerSingleDomainLogic } from '../../crawler_single_domain_logic'; @@ -84,7 +84,7 @@ export const DeduplicationPanel: React.FC = () => { documents on this domain. {documentationLink}." values={{ documentationLink: ( - + {i18n.translate( 'xpack.enterpriseSearch.appSearch.crawler.deduplicationPanel.learnMoreMessage', { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/entry_points_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/entry_points_table.tsx index 4fc7a0569ba0e..d4bfdf77704b8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/entry_points_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/entry_points_table.tsx @@ -14,10 +14,10 @@ import { EuiFieldText, EuiLink, EuiSpacer, EuiText, EuiTitle } from '@elastic/eu import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { docLinks } from '../../../../shared/doc_links'; import { GenericEndpointInlineEditableTable } from '../../../../shared/tables/generic_endpoint_inline_editable_table'; import { InlineEditableTableColumn } from '../../../../shared/tables/inline_editable_table/types'; import { ItemWithAnID } from '../../../../shared/tables/types'; -import { ENTRY_POINTS_DOCS_URL } from '../../../routes'; import { CrawlerDomain, EntryPoint } from '../types'; import { EntryPointsTableLogic } from './entry_points_table_logic'; @@ -80,7 +80,7 @@ export const EntryPointsTable: React.FC = ({ defaultMessage: 'Include the most important URLs for your website here. Entry point URLs will be the first pages to be indexed and processed for links to other pages.', })}{' '} - + {i18n.translate( 'xpack.enterpriseSearch.appSearch.crawler.entryPointsTable.learnMoreLinkText', { defaultMessage: 'Learn more about entry points.' } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/manage_crawls_popover/automatic_crawl_scheduler.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/manage_crawls_popover/automatic_crawl_scheduler.tsx index 4533ca04c75bc..cb0377a471b93 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/manage_crawls_popover/automatic_crawl_scheduler.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/manage_crawls_popover/automatic_crawl_scheduler.tsx @@ -36,8 +36,8 @@ import { MONTHS_UNIT_LABEL, WEEKS_UNIT_LABEL, } from '../../../../../shared/constants/units'; +import { docLinks } from '../../../../../shared/doc_links'; -import { WEB_CRAWLER_DOCS_URL } from '../../../../routes'; import { CrawlUnits } from '../../types'; import { AutomaticCrawlSchedulerLogic } from './automatic_crawl_scheduler_logic'; @@ -81,7 +81,7 @@ export const AutomaticCrawlScheduler: React.FC = () => { defaultMessage="Don't worry about it, we'll start a crawl for you. {readMoreMessage}." values={{ readMoreMessage: ( - + {i18n.translate( 'xpack.enterpriseSearch.appSearch.crawler.automaticCrawlSchedule.readMoreLink', { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview.tsx index 13a13c25a5ad8..d18f40c4c9f23 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview.tsx @@ -13,7 +13,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer, EuiText, EuiTitle } from import { i18n } from '@kbn/i18n'; -import { WEB_CRAWLER_DOCS_URL, WEB_CRAWLER_LOG_DOCS_URL } from '../../routes'; +import { docLinks } from '../../../shared/doc_links'; import { getEngineBreadcrumbs } from '../engine'; import { AppSearchPageTemplate } from '../layout'; @@ -82,7 +82,7 @@ export const CrawlerOverview: React.FC = () => { defaultMessage: "Easily index your website's content. To get started, enter your domain name, provide optional entry points and crawl rules, and we will handle the rest.", })}{' '} - + {i18n.translate( 'xpack.enterpriseSearch.appSearch.crawler.empty.crawlerDocumentationLinkDescription', { @@ -125,7 +125,7 @@ export const CrawlerOverview: React.FC = () => { defaultMessage: "Recent crawl requests are logged here. Using the request ID of each crawl, you can track progress and examine crawl events in Kibana's Discover or Logs user interfaces.", })}{' '} - + {i18n.translate( 'xpack.enterpriseSearch.appSearch.crawler.configurationDocumentationLinkDescription', { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/constants.ts index 9676e7f859ac5..7468597294026 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/constants.ts @@ -7,8 +7,6 @@ import { i18n } from '@kbn/i18n'; -import { AUTHENTICATION_DOCS_URL } from '../../routes'; - export const CREDENTIALS_TITLE = i18n.translate( 'xpack.enterpriseSearch.appSearch.credentials.title', { defaultMessage: 'Credentials' } @@ -108,5 +106,3 @@ export const TOKEN_TYPE_INFO = [ ]; export const FLYOUT_ARIA_LABEL_ID = 'credentialsFlyoutTitle'; - -export const DOCS_HREF = AUTHENTICATION_DOCS_URL; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_flyout/form_components/key_type.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_flyout/form_components/key_type.tsx index 2cf381d8f604f..5213f786dfead 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_flyout/form_components/key_type.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_flyout/form_components/key_type.tsx @@ -12,8 +12,10 @@ import { useValues, useActions } from 'kea'; import { EuiFormRow, EuiSelect, EuiText, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { docLinks } from '../../../../../shared/doc_links'; + import { AppLogic } from '../../../../app_logic'; -import { TOKEN_TYPE_DESCRIPTION, TOKEN_TYPE_INFO, DOCS_HREF } from '../../constants'; +import { TOKEN_TYPE_DESCRIPTION, TOKEN_TYPE_INFO } from '../../constants'; import { CredentialsLogic } from '../../credentials_logic'; export const FormKeyType: React.FC = () => { @@ -36,7 +38,7 @@ export const FormKeyType: React.FC = () => {

{tokenDescription}{' '} - + {i18n.translate('xpack.enterpriseSearch.appSearch.credentials.documentationLink1', { defaultMessage: 'Visit the documentation', })} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.tsx index e351cdf36c657..4f45da9e26046 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.tsx @@ -19,9 +19,9 @@ import { import { i18n } from '@kbn/i18n'; import { EDIT_BUTTON_LABEL, DELETE_BUTTON_LABEL } from '../../../../shared/constants'; +import { docLinks } from '../../../../shared/doc_links'; import { HiddenText } from '../../../../shared/hidden_text'; import { convertMetaToPagination, handlePageChange } from '../../../../shared/table_pagination'; -import { API_KEYS_DOCS_URL } from '../../../routes'; import { TOKEN_TYPE_DISPLAY_NAMES } from '../constants'; import { CredentialsLogic } from '../credentials_logic'; import { ApiToken } from '../types'; @@ -141,7 +141,7 @@ export const CredentialsList: React.FC = () => { defaultMessage: 'Allow applications to access Elastic App Search on your behalf.', })} actions={ - + {i18n.translate('xpack.enterpriseSearch.appSearch.credentials.empty.buttonLabel', { defaultMessage: 'Learn about API keys', })} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/empty_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/empty_state.tsx index 10d81f1623959..363da83d56aac 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/empty_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/empty_state.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { EuiButton, EuiEmptyPrompt } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { CURATIONS_DOCS_URL } from '../../../routes'; +import { docLinks } from '../../../../shared/doc_links'; export const EmptyState: React.FC = () => ( (

} actions={ - + {i18n.translate('xpack.enterpriseSearch.appSearch.engine.curations.empty.buttonLabel', { defaultMessage: 'Read the curations guide', })} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx index 7bbe276aedf69..98da6dc88ef57 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx @@ -30,8 +30,8 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { DocumentCreationLogic } from '..'; import { CANCEL_BUTTON_LABEL } from '../../../../shared/constants'; +import { docLinks } from '../../../../shared/doc_links'; import { getEnterpriseSearchUrl } from '../../../../shared/enterprise_search_url'; -import { API_CLIENTS_DOCS_URL, INDEXING_DOCS_URL } from '../../../routes'; import { EngineLogic } from '../../engine'; import { EngineDetails } from '../../engine/types'; @@ -74,12 +74,12 @@ export const FlyoutBody: React.FC = () => { defaultMessage="The {documentsApiLink} can be used to add new documents to your engine, update documents, retrieve documents by id, and delete documents. There are a variety of {clientLibrariesLink} to help you get started." values={{ documentsApiLink: ( - + documents API ), clientLibrariesLink: ( - + client libraries ), diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx index 07de7b3ec0c34..80e087e007671 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx @@ -26,9 +26,10 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { docLinks } from '../../../shared/doc_links'; import { parseQueryParams } from '../../../shared/query_params'; import { EuiCardTo } from '../../../shared/react_router_helpers'; -import { INDEXING_DOCS_URL, ENGINE_CRAWLER_PATH } from '../../routes'; +import { ENGINE_CRAWLER_PATH } from '../../routes'; import { generateEnginePath } from '../engine'; import illustration from './illustration.svg'; @@ -106,7 +107,7 @@ export const DocumentCreationButtons: React.FC = ({ )} {' '} - + {i18n.translate( 'xpack.enterpriseSearch.appSearch.documentCreation.buttons.emptyStateFooterLink', { defaultMessage: 'Read documentation' } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/components/empty_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/components/empty_state.tsx index 85e834b320751..a311899d380e9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/components/empty_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/components/empty_state.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { EuiButton, EuiEmptyPrompt } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { INDEXING_DOCS_URL } from '../../../routes'; +import { docLinks } from '../../../../shared/doc_links'; export const EmptyState = () => ( (

} actions={ - + {i18n.translate('xpack.enterpriseSearch.appSearch.engine.documents.empty.buttonLabel', { defaultMessage: 'Read the documents guide', })} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx index e31a17406ffd3..e4bcf810d58f8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx @@ -12,8 +12,8 @@ import { useValues } from 'kea'; import { EuiButton, EuiEmptyPrompt, EuiImage, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { docLinks } from '../../../shared/doc_links'; import { EuiButtonTo } from '../../../shared/react_router_helpers'; -import { DOCS_URL } from '../../routes'; import { DocumentCreationButtons, DocumentCreationFlyout } from '../document_creation'; import illustration from '../document_creation/illustration.svg'; @@ -85,7 +85,7 @@ export const EmptyEngineOverview: React.FC = () => { { defaultMessage: 'Engine setup' } ), rightSideItems: [ - + {i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.overview.empty.headingAction', { defaultMessage: 'View documentation' } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_meta_engines_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_meta_engines_state.tsx index 3cf461e3f7d45..0824997ba8896 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_meta_engines_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_meta_engines_state.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { EuiEmptyPrompt, EuiButton } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { META_ENGINES_DOCS_URL } from '../../../routes'; +import { docLinks } from '../../../../shared/doc_links'; export const EmptyMetaEnginesState: React.FC = () => ( (

} actions={ - + {i18n.translate( 'xpack.enterpriseSearch.appSearch.engines.metaEngines.emptyPromptButtonLabel', { defaultMessage: 'Learn more about meta engines' } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/meta_engine_creation/constants.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/meta_engine_creation/constants.tsx index e30868beeb209..8b32ffe77e701 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/meta_engine_creation/constants.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/meta_engine_creation/constants.tsx @@ -11,7 +11,7 @@ import { EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { META_ENGINES_DOCS_URL } from '../../routes'; +import { docLinks } from '../../../shared/doc_links'; export const DEFAULT_LANGUAGE = 'Universal'; @@ -57,7 +57,7 @@ export const META_ENGINE_CREATION_FORM_DOCUMENTATION_DESCRIPTION = ( defaultMessage="{documentationLink} for information about how to get started." values={{ documentationLink: ( - + {META_ENGINE_CREATION_FORM_DOCUMENTATION_LINK} ), diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/empty_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/empty_state.tsx index f17f7a582efdf..b792dec2cba0e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/empty_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/empty_state.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { EuiButton, EuiEmptyPrompt } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { RELEVANCE_DOCS_URL } from '../../../routes'; +import { docLinks } from '../../../../shared/doc_links'; export const EmptyState: React.FC = () => ( ( } )} actions={ - + {i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.relevanceTuning.empty.buttonLabel', { defaultMessage: 'Read the relevance tuning guide' } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/precision_slider/precision_slider.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/precision_slider/precision_slider.tsx index e4b2027aa3d6d..d78949d0fbe74 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/precision_slider/precision_slider.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/precision_slider/precision_slider.tsx @@ -21,7 +21,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { PRECISION_DOCS_URL } from '../../../../routes'; +import { docLinks } from '../../../../../shared/doc_links'; import { RelevanceTuningLogic } from '../../relevance_tuning_logic'; import { STEP_DESCRIPTIONS } from './constants'; @@ -57,7 +57,11 @@ export const PrecisionSlider: React.FC = () => { defaultMessage: 'Fine tune the precision vs. recall settings on your engine.', } )}{' '} - + {i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.relevanceTuning.precisionSlider.learnMore.link', { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_callouts.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_callouts.tsx index bf2c21a1003f5..ef0bea39439c5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_callouts.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_callouts.tsx @@ -13,8 +13,9 @@ import { EuiCallOut, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { docLinks } from '../../../shared/doc_links'; import { EuiLinkTo } from '../../../shared/react_router_helpers'; -import { META_ENGINES_DOCS_URL, ENGINE_SCHEMA_PATH } from '../../routes'; +import { ENGINE_SCHEMA_PATH } from '../../routes'; import { EngineLogic, generateEnginePath } from '../engine'; import { RelevanceTuningLogic } from '.'; @@ -98,7 +99,7 @@ export const RelevanceTuningCallouts: React.FC = () => { values={{ schemaFieldsWithConflictsCount, link: ( - + {i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.relevanceTuning.whatsThisLinkLabel', { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/components/empty_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/components/empty_state.tsx index 7f91447b910b6..6434a877ead5e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/components/empty_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/components/empty_state.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { EuiButton, EuiEmptyPrompt } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { RESULT_SETTINGS_DOCS_URL } from '../../../routes'; +import { docLinks } from '../../../../shared/doc_links'; export const EmptyState: React.FC = () => ( ( } )} actions={ - + {i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.resultSettings.empty.buttonLabel', { defaultMessage: 'Read the result settings guide' } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings.tsx index aff138b9c3884..2ffe6cb357e54 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings.tsx @@ -12,6 +12,7 @@ import { useActions, useValues } from 'kea'; import { EuiSpacer } from '@elastic/eui'; import { APP_SEARCH_PLUGIN } from '../../../../../common/constants'; +import { docLinks } from '../../../shared/doc_links'; import { RoleMappingsTable, RoleMappingsHeading, @@ -22,7 +23,6 @@ import { } from '../../../shared/role_mapping'; import { ROLE_MAPPINGS_TITLE } from '../../../shared/role_mapping/constants'; -import { SECURITY_DOCS_URL } from '../../routes'; import { AppSearchPageTemplate } from '../layout'; import { ROLE_MAPPINGS_ENGINE_ACCESS_HEADING } from './constants'; @@ -57,7 +57,7 @@ export const RoleMappings: React.FC = () => { const rolesEmptyState = ( ); @@ -66,7 +66,7 @@ export const RoleMappings: React.FC = () => {
initializeRoleMapping()} /> { @@ -40,7 +40,12 @@ export const EmptyState: React.FC = () => {

} actions={ - + {i18n.translate('xpack.enterpriseSearch.appSearch.engine.schema.empty.buttonLabel', { defaultMessage: 'Read the indexing schema guide', })} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/empty_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/empty_state.tsx index 9a663e1372211..e5b0f2facedbd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/empty_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/empty_state.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { EuiButton, EuiEmptyPrompt } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { SEARCH_UI_DOCS_URL } from '../../../routes'; +import { docLinks } from '../../../../shared/doc_links'; export const EmptyState: React.FC = () => ( (

} actions={ - + {i18n.translate('xpack.enterpriseSearch.appSearch.engine.searchUI.empty.buttonLabel', { defaultMessage: 'Read the Search UI guide', })} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/search_ui.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/search_ui.tsx index d7398357a5e58..cfef71d34fb9f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/search_ui.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/search_ui.tsx @@ -12,7 +12,7 @@ import { useActions, useValues } from 'kea'; import { EuiText, EuiFlexItem, EuiFlexGroup, EuiSpacer, EuiLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { SEARCH_UI_DOCS_URL } from '../../routes'; +import { docLinks } from '../../../shared/doc_links'; import { EngineLogic, getEngineBreadcrumbs } from '../engine'; import { AppSearchPageTemplate } from '../layout'; @@ -63,7 +63,7 @@ export const SearchUI: React.FC = () => { defaultMessage="Use the fields below to generate a sample search experience built with Search UI. Use the sample to preview search results, or build upon it to create your own custom search experience. {link}." values={{ link: ( - + { defaultMessage: 'Log retention is determined by the ILM policies for your deployment.', })}
- + {i18n.translate('xpack.enterpriseSearch.appSearch.settings.logRetention.learnMore', { defaultMessage: 'Learn more about log retention for Enterprise Search.', })} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/synonyms/components/empty_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/synonyms/components/empty_state.tsx index ef5e1dafa443f..ff7e8ce16c6d2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/synonyms/components/empty_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/synonyms/components/empty_state.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { EuiEmptyPrompt, EuiButton } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { SYNONYMS_DOCS_URL } from '../../../routes'; +import { docLinks } from '../../../../shared/doc_links'; import { SynonymModal, SynonymIcon } from '.'; @@ -35,7 +35,7 @@ export const EmptyState: React.FC = () => {

} actions={ - + {i18n.translate('xpack.enterpriseSearch.appSearch.engine.synonyms.empty.buttonLabel', { defaultMessage: 'Read the synonyms guide', })} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts index 1a41004c882e3..128af5adacfad 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts @@ -5,30 +5,6 @@ * 2.0. */ -import { docLinks } from '../shared/doc_links'; - -export const API_DOCS_URL = docLinks.appSearchApis; -export const API_CLIENTS_DOCS_URL = docLinks.appSearchApiClients; -export const API_KEYS_DOCS_URL = docLinks.appSearchApiKeys; -export const AUTHENTICATION_DOCS_URL = docLinks.appSearchAuthentication; -export const CRAWL_RULES_DOCS_URL = docLinks.appSearchCrawlRules; -export const CURATIONS_DOCS_URL = docLinks.appSearchCurations; -export const DOCS_URL = docLinks.appSearchGuide; -export const DUPLICATE_DOCS_URL = docLinks.appSearchDuplicateDocuments; -export const ENTRY_POINTS_DOCS_URL = docLinks.appSearchEntryPoints; -export const INDEXING_DOCS_URL = docLinks.appSearchIndexingDocs; -export const INDEXING_SCHEMA_DOCS_URL = docLinks.appSearchIndexingDocsSchema; -export const LOG_SETTINGS_DOCS_URL = docLinks.appSearchLogSettings; -export const META_ENGINES_DOCS_URL = docLinks.appSearchMetaEngines; -export const PRECISION_DOCS_URL = docLinks.appSearchPrecision; -export const RELEVANCE_DOCS_URL = docLinks.appSearchRelevance; -export const RESULT_SETTINGS_DOCS_URL = docLinks.appSearchResultSettings; -export const SEARCH_UI_DOCS_URL = docLinks.appSearchSearchUI; -export const SECURITY_DOCS_URL = docLinks.appSearchSecurity; -export const SYNONYMS_DOCS_URL = docLinks.appSearchSynonyms; -export const WEB_CRAWLER_DOCS_URL = docLinks.appSearchWebCrawler; -export const WEB_CRAWLER_LOG_DOCS_URL = docLinks.appSearchWebCrawlerEventLogs; - export const ROOT_PATH = '/'; export const SETUP_GUIDE_PATH = '/setup_guide'; export const LIBRARY_PATH = '/library'; diff --git a/x-pack/plugins/enterprise_search/public/applications/applications/components/playground/page_template.tsx b/x-pack/plugins/enterprise_search/public/applications/applications/components/playground/page_template.tsx new file mode 100644 index 0000000000000..40698b273730b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/applications/components/playground/page_template.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 React, { useLayoutEffect } from 'react'; + +import { useValues } from 'kea'; + +import useObservable from 'react-use/lib/useObservable'; + +import { SEARCH_PRODUCT_NAME } from '../../../../../common/constants'; +import { KibanaLogic } from '../../../shared/kibana'; +import { SetSearchPlaygroundChrome } from '../../../shared/kibana_chrome/set_chrome'; +import { EnterpriseSearchPageTemplateWrapper, PageTemplateProps } from '../../../shared/layout'; +import { useEnterpriseSearchNav } from '../../../shared/layout'; +import { SendEnterpriseSearchTelemetry } from '../../../shared/telemetry'; + +import { PlaygroundHeaderDocsAction } from './header_docs_action'; + +export type SearchPlaygroundPageTemplateProps = Omit< + PageTemplateProps, + 'useEndpointHeaderActions' +> & { + hasSchemaConflicts?: boolean; + restrictWidth?: boolean; + searchApplicationName?: string; +}; + +export const SearchPlaygroundPageTemplate: React.FC = ({ + children, + pageChrome, + pageViewTelemetry, + searchApplicationName, + hasSchemaConflicts, + restrictWidth = true, + ...pageTemplateProps +}) => { + const navItems = useEnterpriseSearchNav(); + + const { renderHeaderActions, getChromeStyle$ } = useValues(KibanaLogic); + const chromeStyle = useObservable(getChromeStyle$(), 'classic'); + + useLayoutEffect(() => { + renderHeaderActions(PlaygroundHeaderDocsAction); + + return () => { + renderHeaderActions(); + }; + }, []); + + return ( + } + useEndpointHeaderActions={false} + > + {pageViewTelemetry && ( + + )} + {children} + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/applications/components/playground/playground.tsx b/x-pack/plugins/enterprise_search/public/applications/applications/components/playground/playground.tsx index b117518d3a6e0..e8e72e5dfb37a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/applications/components/playground/playground.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/applications/components/playground/playground.tsx @@ -12,7 +12,8 @@ import { useValues } from 'kea'; import { i18n } from '@kbn/i18n'; import { KibanaLogic } from '../../../shared/kibana'; -import { EnterpriseSearchApplicationsPageTemplate } from '../layout/page_template'; + +import { SearchPlaygroundPageTemplate } from './page_template'; export const Playground: React.FC = () => { const { searchPlayground } = useValues(KibanaLogic); @@ -22,7 +23,7 @@ export const Playground: React.FC = () => { } return ( - { panelled={false} customPageSections bottomBorder="extended" - docLink="playground" > - + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/elasticsearch_guide.tsx b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/elasticsearch_guide.tsx index 80a8de9acdc21..be470577cd519 100644 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/elasticsearch_guide.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/elasticsearch_guide.tsx @@ -40,7 +40,7 @@ export const ElasticsearchGuide = () => { }, []); return ( - + {isFlyoutOpen && setIsFlyoutOpen(false)} />}

diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/layout/page_template.tsx b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/layout/page_template.tsx index 7f2eded8a6565..c5c777cb74773 100644 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/layout/page_template.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/layout/page_template.tsx @@ -19,13 +19,14 @@ export const EnterpriseSearchElasticsearchPageTemplate: React.FC { + const navItems = useEnterpriseSearchNav(); return ( } > diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/components/generated_config_fields.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/components/generated_config_fields.tsx index 133c15f97f61c..53cbf579a940f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/components/generated_config_fields.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/components/generated_config_fields.tsx @@ -156,6 +156,10 @@ export const GeneratedConfigFields: React.FC = ({ data-test-subj="enterpriseSearchConnectorDeploymentButton" iconType="copyClipboard" onClick={copy} + aria-label={i18n.translate( + 'xpack.enterpriseSearch.connectorDeployment.copyConnectorId', + { defaultMessage: 'Copy connector ID' } + )} /> )} @@ -237,6 +241,10 @@ export const GeneratedConfigFields: React.FC = ({ isLoading={isGenerateLoading} onClick={refreshButtonClick} disabled={!connector.index_name} + aria-label={i18n.translate( + 'xpack.enterpriseSearch.connectorDeployment.refreshAPIKey', + { defaultMessage: 'Refresh an Elasticsearch API key' } + )} /> )} @@ -246,6 +254,10 @@ export const GeneratedConfigFields: React.FC = ({ data-test-subj="enterpriseSearchConnectorDeploymentButton" iconType="copyClipboard" onClick={copy} + aria-label={i18n.translate( + 'xpack.enterpriseSearch.connectorDeployment.copyIndexName', + { defaultMessage: 'Copy index name' } + )} /> diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/components/manual_configuration.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/components/manual_configuration.tsx index 13273266a2068..825f47920d256 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/components/manual_configuration.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/components/manual_configuration.tsx @@ -6,16 +6,27 @@ */ import React, { useState } from 'react'; +import { css } from '@emotion/react'; +import dedent from 'dedent'; + +import { useValues } from 'kea'; + import { EuiButtonIcon, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover, useGeneratedHtmlId, + useEuiTheme, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { NATIVE_CONNECTOR_DEFINITIONS, NativeConnector } from '@kbn/search-connectors'; +import { TryInConsoleButton } from '@kbn/try-in-console'; +import { KibanaDeps } from '../../../../../../../common/types'; +import { NewConnectorLogic } from '../../../new_index/method_connector/new_connector_logic'; import { SelfManagePreference } from '../create_connector'; import { ManualConfigurationFlyout } from './manual_configuration_flyout'; @@ -25,10 +36,18 @@ export interface ManualConfigurationProps { selfManagePreference: SelfManagePreference; } +interface ConnectorConfiguration { + [key: string]: { + value: string; + }; +} + export const ManualConfiguration: React.FC = ({ isDisabled, selfManagePreference, }) => { + const { euiTheme } = useEuiTheme(); + const { services } = useKibana(); const [isPopoverOpen, setPopover] = useState(false); const splitButtonPopoverId = useGeneratedHtmlId({ prefix: 'splitButtonPopover', @@ -40,9 +59,104 @@ export const ManualConfiguration: React.FC = ({ const closePopover = () => { setPopover(false); }; - + const { selectedConnector, rawName } = useValues(NewConnectorLogic); const [isFlyoutVisible, setIsFlyoutVisible] = useState(false); const [flyoutContent, setFlyoutContent] = useState<'manual_config' | 'client'>(); + const getCodeSnippet = (): string => { + const connectorInfo: NativeConnector | undefined = selectedConnector?.serviceType + ? NATIVE_CONNECTOR_DEFINITIONS[selectedConnector.serviceType] + : undefined; + if (!connectorInfo) { + return ''; + } + + const dynamicConfigValues = Object.entries( + connectorInfo.configuration as ConnectorConfiguration + ) + .map(([key, config]) => { + const defaultValue = config ? JSON.stringify(config.value) : null; + return ` "${key}": ${defaultValue}`; + }) + .join(',\n'); + const CONSOLE_SNIPPET = dedent` # Example of how to create a ${connectorInfo?.name} connector using the API +# This also creates related resources like an index and an API key. +# This is an alternative to using the UI creation flow. + +# 1. Create an index +PUT connector-${rawName} +{ + "settings": { + "index": { + "number_of_shards": 3, + "number_of_replicas": 2 + } + } +} +# 2. Create a connector +PUT _connector/${rawName} +{ + "name": "My ${connectorInfo?.name} connector", + "index_name": "connector-${rawName}", + "service_type": "${selectedConnector?.serviceType}" +} +# 3. Create an API key +POST /_security/api_key +{ + "name": "${rawName}-api-key", + "role_descriptors": { + "${selectedConnector?.serviceType}-api-key-role": { + "cluster": [ + "monitor", + "manage_connector" + ], + "indices": [ + { + "names": [ + "connector-${rawName}", + ".search-acl-filter-connector-${rawName}", + ".elastic-connectors*" + ], + "privileges": [ + "all" + ], + "allow_restricted_indices": false + } + ] + } + } +} + +# 🔧 Configure your connector +# NOTE: Configuration keys differ per service type. +PUT _connector/${rawName}/_configuration +{ + "values": { +${dynamicConfigValues} + } +} + +# 🔌 Verify your connector is connected +GET _connector/${rawName} + +# 🔄 Sync data +POST _connector/_sync_job +{ + "id": "${rawName}", + "job_type": "full" +} + +# ⏳ Check sync status +GET _connector/_sync_job?connector_id=${rawName}&size=1 + +# Once the job completes, the status should return completed +# 🎉 Verify that data is present in the index with the following API call +GET connector-${rawName}/_count + +# 🔎 Elasticsearch stores data in documents, which are JSON objects. List the individual documents with the following API call +GET connector-${rawName}/_search +`; + return CONSOLE_SNIPPET; + }; const items = [ = ({ { defaultMessage: 'Manual configuration' } )} , + { + closePopover(); + }} + css={css` + .euiLink { + color: ${euiTheme.colors.text}; + font-weight: ${euiTheme.font.weight.regular}; + } + `} + > + + , = ({ title, setCurrentStep }) => { const { connector } = useValues(ConnectorViewLogic); const { updateConnectorConfiguration } = useActions(ConnectorViewLogic); + const { setFormDirty } = useActions(NewConnectorLogic); const { status } = useValues(ConnectorConfigurationApiLogic); const isSyncing = false; @@ -109,7 +111,10 @@ export const ConfigurationStep: React.FC = ({ title, set setCurrentStep('finish')} + onClick={() => { + setFormDirty(false); + setCurrentStep('finish'); + }} fill > {Constants.NEXT_BUTTON_LABEL} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/create_connector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/create_connector.tsx index e8cef81662096..6e83bf98c2371 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/create_connector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/create_connector.tsx @@ -28,6 +28,11 @@ import { import { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; import { i18n } from '@kbn/i18n'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { useUnsavedChangesPrompt } from '@kbn/unsaved-changes-prompt'; + +import { HttpLogic } from '../../../../shared/http'; +import { KibanaLogic } from '../../../../shared/kibana'; import { AddConnectorApiLogic } from '../../../api/connector/add_connector_api_logic'; import { EnterpriseSearchContentPageTemplate } from '../../layout'; @@ -47,20 +52,23 @@ import { StartStep } from './start_step'; export type ConnectorCreationSteps = 'start' | 'deployment' | 'configure' | 'finish'; export type SelfManagePreference = 'native' | 'selfManaged'; export const CreateConnector: React.FC = () => { + const { overlays } = useKibana().services; + + const { http } = useValues(HttpLogic); + const { application, history } = useValues(KibanaLogic); + const { error } = useValues(AddConnectorApiLogic); const { euiTheme } = useEuiTheme(); const [selfManagePreference, setSelfManagePreference] = useState('native'); - const { selectedConnector, currentStep } = useValues(NewConnectorLogic); + const { selectedConnector, currentStep, isFormDirty } = useValues(NewConnectorLogic); const { setCurrentStep } = useActions(NewConnectorLogic); const stepStates = generateStepState(currentStep); useEffect(() => { // TODO: separate this to ability and preference - if (!selectedConnector?.isNative || !selfManagePreference) { + if (selectedConnector && !selectedConnector.isNative && selfManagePreference === 'native') { setSelfManagePreference('selfManaged'); - } else { - setSelfManagePreference('native'); } }, [selectedConnector]); @@ -137,6 +145,33 @@ export const CreateConnector: React.FC = () => { ), }; + useUnsavedChangesPrompt({ + cancelButtonText: i18n.translate( + 'xpack.enterpriseSearch.createConnector.unsavedPrompt.cancel', + { + defaultMessage: 'Continue setup', + } + ), + confirmButtonText: i18n.translate( + 'xpack.enterpriseSearch.createConnector.unsavedPrompt.confirm', + { + defaultMessage: 'Leave the page', + } + ), + hasUnsavedChanges: isFormDirty, + history, + http, + messageText: i18n.translate('xpack.enterpriseSearch.createConnector.unsavedPrompt.body', { + defaultMessage: + 'Your connector is created but missing some details. You can complete the setup later in the connector configuration page, but this guided flow offers more help.', + }), + navigateToUrl: application.navigateToUrl, + openConfirm: overlays?.openConfirm ?? (() => Promise.resolve(false)), + titleText: i18n.translate('xpack.enterpriseSearch.createConnector.unsavedPrompt.title', { + defaultMessage: 'Your connector is not fully configured', + }), + }); + return ( { - {selfManagePreference + {selfManagePreference === 'selfManaged' ? i18n.translate( 'xpack.enterpriseSearch.createConnector.badgeType.selfManaged', { - defaultMessage: 'Self managed', + defaultMessage: 'Self-managed', } ) : i18n.translate( diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/start_step.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/start_step.tsx index 28d04750b80ba..7e23474b207f1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/start_step.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/start_step.tsx @@ -27,6 +27,7 @@ import { import { i18n } from '@kbn/i18n'; import * as Constants from '../../../../shared/constants'; +import { isValidIndexName } from '../../../utils/validate_index_name'; import { GeneratedConfigFields } from '../../connector_detail/components/generated_config_fields'; import { ConnectorViewLogic } from '../../connector_detail/connector_view_logic'; @@ -63,13 +64,26 @@ export const StartStep: React.FC = ({ isGenerateLoading, isCreateLoading, } = useValues(NewConnectorLogic); - const { setRawName, createConnector, generateConnectorName } = useActions(NewConnectorLogic); + const { setRawName, createConnector, generateConnectorName, setFormDirty } = + useActions(NewConnectorLogic); const { connector } = useValues(ConnectorViewLogic); const handleNameChange = (e: ChangeEvent) => { setRawName(e.target.value); }; + const formError = isValidIndexName(rawName) + ? error + : i18n.translate( + 'xpack.enterpriseSearch.createConnector.startStep.euiFormRow.nameInputHelpText.lineOne', + { + defaultMessage: '{connectorName} is an invalid index name', + values: { + connectorName: rawName, + }, + } + ); + return ( @@ -99,6 +113,22 @@ export const StartStep: React.FC = ({ 'xpack.enterpriseSearch.createConnector.startStep.euiFormRow.connectorNameLabel', { defaultMessage: 'Connector name' } )} + helpText={ + <> + + {formError} + + + {i18n.translate( + 'xpack.enterpriseSearch.startStep.namesShouldBeLowercaseTextLabel', + { + defaultMessage: + 'The connector name should be lowercase and cannot contain spaces or special characters.', + } + )} + + + } > = ({ 'xpack.enterpriseSearch.createConnector.startStep.euiFormRow.descriptionLabel', { defaultMessage: 'Description' } )} + labelAppend={ + + {i18n.translate( + 'xpack.enterpriseSearch.createConnector.startStep.euiFormRow.descriptionLabelAppend', + { defaultMessage: 'Optional' } + )} + + } > = ({ hasShadow={false} hasBorder paddingSize="l" - color={selectedConnector?.name ? 'plain' : 'subdued'} + color={ + selectedConnector?.name && isValidIndexName(rawName) && !error ? 'plain' : 'subdued' + } > - +

{i18n.translate( 'xpack.enterpriseSearch.createConnector.startStep.h4.deploymentLabel', @@ -217,7 +263,10 @@ export const StartStep: React.FC = ({

- +

{i18n.translate( 'xpack.enterpriseSearch.createConnector.startStep.p.youWillStartTheLabel', @@ -236,11 +285,12 @@ export const StartStep: React.FC = ({ createConnector({ isSelfManaged: true, }); + setFormDirty(true); setCurrentStep('deployment'); } }} fill - disabled={!canConfigureConnector} + disabled={!canConfigureConnector || !isValidIndexName(rawName) || Boolean(error)} isLoading={isCreateLoading || isGenerateLoading} > {Constants.NEXT_BUTTON_LABEL} @@ -250,12 +300,20 @@ export const StartStep: React.FC = ({ ) : ( - +

{i18n.translate( 'xpack.enterpriseSearch.createConnector.startStep.h4.configureIndexAndAPILabel', @@ -266,7 +324,14 @@ export const StartStep: React.FC = ({

- +

{i18n.translate( 'xpack.enterpriseSearch.createConnector.startStep.p.thisProcessWillCreateLabel', @@ -294,7 +359,9 @@ export const StartStep: React.FC = ({ setCurrentStep('configure')} + onClick={() => { + setCurrentStep('configure'); + }} > {Constants.NEXT_BUTTON_LABEL} @@ -305,11 +372,14 @@ export const StartStep: React.FC = ({ { + setFormDirty(true); createConnector({ isSelfManaged: false, }); @@ -325,7 +395,13 @@ export const StartStep: React.FC = ({ diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/new_connector_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/new_connector_logic.ts index 796a2a64ab56c..0d21db6e03baf 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/new_connector_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/new_connector_logic.ts @@ -56,6 +56,7 @@ export interface NewConnectorValues { | undefined; generatedNameData: GenerateConnectorNamesApiResponse | undefined; isCreateLoading: boolean; + isFormDirty: boolean; isGenerateLoading: boolean; rawName: string; selectedConnector: ConnectorDefinition | null; @@ -85,6 +86,7 @@ type NewConnectorActions = { createConnectorApi: AddConnectorApiLogicActions['makeRequest']; fetchConnector: ConnectorViewActions['fetchConnector']; setCurrentStep(step: ConnectorCreationSteps): { step: ConnectorCreationSteps }; + setFormDirty: (isDirty: boolean) => { isDirty: boolean }; setRawName(rawName: string): { rawName: string }; setSelectedConnector(connector: ConnectorDefinition | null): { connector: ConnectorDefinition | null; @@ -103,6 +105,7 @@ export const NewConnectorLogic = kea ({ step }), + setFormDirty: (isDirty) => ({ isDirty }), setRawName: (rawName) => ({ rawName }), setSelectedConnector: (connector) => ({ connector }), }, @@ -214,6 +217,13 @@ export const NewConnectorLogic = kea step, }, ], + isFormDirty: [ + false, // Initial state (form is not dirty) + { + // @ts-expect-error upgrade typescript v5.1.6 + setFormDirty: (_, { isDirty }) => isDirty, + }, + ], rawName: [ '', { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/generate_api_key_modal/modal.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/generate_api_key_modal/modal.tsx index 18514ef93d9d9..d19568bea9e3c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/generate_api_key_modal/modal.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/generate_api_key_modal/modal.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { useRef, useEffect } from 'react'; import { useValues, useActions } from 'kea'; @@ -26,11 +26,12 @@ import { EuiText, EuiSpacer, EuiLink, - EuiFormLabel, EuiCodeBlock, + EuiCallOut, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; import { docLinks } from '../../../../../shared/doc_links'; @@ -49,6 +50,13 @@ export const GenerateApiKeyModal: React.FC = ({ indexN const { ingestionMethod } = useValues(IndexViewLogic); const { setKeyName } = useActions(GenerateApiKeyModalLogic); const { makeRequest } = useActions(GenerateApiKeyLogic); + const copyApiKeyRef = useRef(null); + + useEffect(() => { + if (isSuccess) { + copyApiKeyRef.current?.focus(); + } + }, [isSuccess]); return ( @@ -68,7 +76,11 @@ export const GenerateApiKeyModal: React.FC = ({ indexN "Before you can start posting documents to your Elasticsearch index you'll need to create at least one API key.", })}   - + {i18n.translate( 'xpack.enterpriseSearch.content.overview.generateApiKeyModal.learnMore', { defaultMessage: 'Learn more about API keys' } @@ -77,15 +89,25 @@ export const GenerateApiKeyModal: React.FC = ({ indexN

- + + {!isSuccess ? ( <> - + + } + fullWidth + > = ({ indexN ) : ( - {keyName} - + {keyName}, + }} + /> + } + color="success" + iconType="check" + role="alert" + /> = ({ indexN = ({ indexN diff --git a/x-pack/plugins/enterprise_search/public/applications/index.tsx b/x-pack/plugins/enterprise_search/public/applications/index.tsx index eafa8827869d8..717379d433dd1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.tsx @@ -114,6 +114,7 @@ export const renderApp = ( data: plugins.data, esConfig, getChromeStyle$: chrome.getChromeStyle$, + getNavLinks: chrome.navLinks.getAll, guidedOnboarding, history, indexMappingComponent, diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/api_key/api_key_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/api_key/api_key_panel.tsx index 34c7ac66343c9..b9d5cf8c414d6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/api_key/api_key_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/api_key/api_key_panel.tsx @@ -76,6 +76,7 @@ export const ApiKeyPanel: React.FC = () => { {(copy) => ( { {(copy) => ( { - - - - 0 ? 'success' : 'warning'} - data-test-subj="api-keys-count-badge" - > - {apiKeys.length} -
- ), - }} - /> - - - + 0 ? 'success' : 'warning'} + data-test-subj="api-keys-count-badge" + > + + diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/api_key/create_api_key_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/api_key/create_api_key_flyout.tsx index 38217df269fd1..c72f56c656e49 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/api_key/create_api_key_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/api_key/create_api_key_flyout.tsx @@ -210,6 +210,7 @@ export const CreateApiKeyFlyout: React.FC = ({ onClose defaultMessage: 'Store this API key', })} titleSize="xs" + role="alert" > {i18n.translate('xpack.enterpriseSearch.apiKey.apiKeyStepDescription', { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/api_key_panel_content.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/api_key_panel_content.tsx index ff271a3a3d79e..a220e077f0824 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/api_key_panel_content.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/api_key_panel_content.tsx @@ -102,26 +102,18 @@ export const ApiKeyPanelContent: React.FC = ({ apiKeys, open - - - - 0 ? 'success' : 'warning'} - data-test-subj="api-keys-count-badge" - > - {apiKeys?.length || 0} - - ), - }} - /> - - - + 0 ? 'success' : 'warning'} + data-test-subj="api-keys-count-badge" + > + + diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts index f74345a1c75c1..6cd6e5410ef11 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts @@ -55,6 +55,7 @@ export interface KibanaLogicProps { data?: DataPublicPluginStart; esConfig: ESConfig; getChromeStyle$: ChromeStart['getChromeStyle$']; + getNavLinks: ChromeStart['navLinks']['getAll']; guidedOnboarding?: GuidedOnboardingPluginStart; history: ScopedHistory; indexMappingComponent?: React.FC; @@ -87,6 +88,7 @@ export interface KibanaValues { data: DataPublicPluginStart | null; esConfig: ESConfig; getChromeStyle$: ChromeStart['getChromeStyle$']; + getNavLinks: ChromeStart['navLinks']['getAll']; guidedOnboarding: GuidedOnboardingPluginStart | null; history: ScopedHistory; indexMappingComponent: React.FC | null; @@ -126,6 +128,7 @@ export const KibanaLogic = kea>({ data: [props.data || null, {}], esConfig: [props.esConfig || { elasticsearch_host: ELASTICSEARCH_URL_PLACEHOLDER }, {}], getChromeStyle$: [props.getChromeStyle$, {}], + getNavLinks: [props.getNavLinks, {}], guidedOnboarding: [props.guidedOnboarding || null, {}], history: [props.history, {}], indexMappingComponent: [props.indexMappingComponent || null, {}], diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts index ea6bda26be450..189ca53e362e1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts @@ -22,6 +22,8 @@ import { VECTOR_SEARCH_PLUGIN, WORKPLACE_SEARCH_PLUGIN, SEMANTIC_SEARCH_PLUGIN, + APPLICATIONS_PLUGIN, + GETTING_STARTED_TITLE, } from '../../../../common/constants'; import { stripLeadingSlash } from '../../../../common/strip_slashes'; @@ -126,7 +128,11 @@ export const useEnterpriseSearchBreadcrumbs = (breadcrumbs: Breadcrumbs = []) => ]); export const useAnalyticsBreadcrumbs = (breadcrumbs: Breadcrumbs = []) => - useSearchBreadcrumbs([{ text: ANALYTICS_PLUGIN.NAME, path: '/' }, ...breadcrumbs]); + useSearchBreadcrumbs([ + { text: APPLICATIONS_PLUGIN.NAV_TITLE }, + { text: ANALYTICS_PLUGIN.NAME, path: '/' }, + ...breadcrumbs, + ]); export const useElasticsearchBreadcrumbs = (breadcrumbs: Breadcrumbs = []) => useSearchBreadcrumbs([ @@ -161,13 +167,25 @@ export const useSearchExperiencesBreadcrumbs = (breadcrumbs: Breadcrumbs = []) = useSearchBreadcrumbs([{ text: SEARCH_EXPERIENCES_PLUGIN.NAV_TITLE, path: '/' }, ...breadcrumbs]); export const useEnterpriseSearchApplicationsBreadcrumbs = (breadcrumbs: Breadcrumbs = []) => - useSearchBreadcrumbs(breadcrumbs); + useSearchBreadcrumbs([{ text: APPLICATIONS_PLUGIN.NAV_TITLE }, ...breadcrumbs]); export const useAiSearchBreadcrumbs = (breadcrumbs: Breadcrumbs = []) => - useSearchBreadcrumbs([{ text: AI_SEARCH_PLUGIN.NAME, path: '/' }, ...breadcrumbs]); + useSearchBreadcrumbs([ + { text: GETTING_STARTED_TITLE }, + { text: AI_SEARCH_PLUGIN.NAME, path: '/' }, + ...breadcrumbs, + ]); export const useVectorSearchBreadcrumbs = (breadcrumbs: Breadcrumbs = []) => - useSearchBreadcrumbs([{ text: VECTOR_SEARCH_PLUGIN.NAV_TITLE, path: '/' }, ...breadcrumbs]); + useSearchBreadcrumbs([ + { text: GETTING_STARTED_TITLE }, + { text: VECTOR_SEARCH_PLUGIN.NAV_TITLE, path: '/' }, + ...breadcrumbs, + ]); export const useSemanticSearchBreadcrumbs = (breadcrumbs: Breadcrumbs = []) => - useSearchBreadcrumbs([{ text: SEMANTIC_SEARCH_PLUGIN.NAME, path: '/' }, ...breadcrumbs]); + useSearchBreadcrumbs([ + { text: GETTING_STARTED_TITLE }, + { text: SEMANTIC_SEARCH_PLUGIN.NAME, path: '/' }, + ...breadcrumbs, + ]); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_title.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_title.ts index eaeb30f1540d0..df7d16cddc4d4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_title.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_title.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { i18n } from '@kbn/i18n'; + import { AI_SEARCH_PLUGIN, ANALYTICS_PLUGIN, @@ -40,7 +42,12 @@ export const searchTitle = (page: Title = []) => generateTitle([...page, SEARCH_ export const analyticsTitle = (page: Title = []) => generateTitle([...page, ANALYTICS_PLUGIN.NAME]); export const elasticsearchTitle = (page: Title = []) => - generateTitle([...page, 'Getting started with Elasticsearch']); + generateTitle([ + ...page, + i18n.translate('xpack.enterpriseSearch.titles.elasticsearch', { + defaultMessage: 'Getting started with Elasticsearch', + }), + ]); export const appSearchTitle = (page: Title = []) => generateTitle([...page, APP_SEARCH_PLUGIN.NAME]); @@ -61,3 +68,11 @@ export const semanticSearchTitle = (page: Title = []) => export const enterpriseSearchContentTitle = (page: Title = []) => generateTitle([...page, ENTERPRISE_SEARCH_CONTENT_PLUGIN.NAME]); + +export const searchApplicationsTitle = (page: Title = []) => + generateTitle([ + ...page, + i18n.translate('xpack.enterpriseSearch.titles.searchApplications', { + defaultMessage: 'Search Applications', + }), + ]); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx index 8f7c71d1309c0..0c05cb0c02ca0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx @@ -9,8 +9,7 @@ import React, { useEffect } from 'react'; import { useValues } from 'kea'; -import { APPLICATIONS_PLUGIN } from '../../../../common/constants'; - +import { SEARCH_APPS_BREADCRUMB } from '../../../../common/constants'; import { KibanaLogic } from '../kibana'; import { @@ -35,6 +34,8 @@ import { appSearchTitle, elasticsearchTitle, enterpriseSearchContentTitle, + generateTitle, + searchApplicationsTitle, searchExperiencesTitle, searchTitle, semanticSearchTitle, @@ -210,14 +211,30 @@ export const SetSearchExperiencesChrome: React.FC = ({ trail = [ return null; }; +export const SetSearchPlaygroundChrome: React.FC = ({ trail = [] }) => { + const { setBreadcrumbs, setDocTitle } = useValues(KibanaLogic); + + const title = reverseArray(trail); + const docTitle = generateTitle(title); + + const breadcrumbs = useEnterpriseSearchApplicationsBreadcrumbs(useGenerateBreadcrumbs(trail)); + + useEffect(() => { + setBreadcrumbs(breadcrumbs); + setDocTitle(docTitle); + }, [trail]); + + return null; +}; + export const SetEnterpriseSearchApplicationsChrome: React.FC = ({ trail = [] }) => { const { setBreadcrumbs, setDocTitle } = useValues(KibanaLogic); const title = reverseArray(trail); - const docTitle = appSearchTitle(title); + const docTitle = searchApplicationsTitle(title); const breadcrumbs = useEnterpriseSearchApplicationsBreadcrumbs( - useGenerateBreadcrumbs([APPLICATIONS_PLUGIN.NAV_TITLE, ...trail]) + useGenerateBreadcrumbs([SEARCH_APPS_BREADCRUMB, ...trail]) ); useEffect(() => { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/base_nav.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/base_nav.tsx new file mode 100644 index 0000000000000..b971ab6deff53 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/base_nav.tsx @@ -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 React from 'react'; + +import { EuiText } from '@elastic/eui'; +import { + ENTERPRISE_SEARCH_APP_ID, + ENTERPRISE_SEARCH_ANALYTICS_APP_ID, + SEARCH_ELASTICSEARCH, + SEARCH_VECTOR_SEARCH, + SEARCH_SEMANTIC_SEARCH, + SEARCH_AI_SEARCH, +} from '@kbn/deeplinks-search'; +import { i18n } from '@kbn/i18n'; + +import { GETTING_STARTED_TITLE } from '../../../../common/constants'; + +import { ClassicNavItem, BuildClassicNavParameters } from '../types'; + +export const buildBaseClassicNavItems = ({ + productAccess, +}: BuildClassicNavParameters): ClassicNavItem[] => { + const navItems: ClassicNavItem[] = []; + + // Home + navItems.push({ + 'data-test-subj': 'searchSideNav-Home', + deepLink: { + link: ENTERPRISE_SEARCH_APP_ID, + shouldShowActiveForSubroutes: true, + }, + id: 'home', + name: ( + + {i18n.translate('xpack.enterpriseSearch.nav.homeTitle', { + defaultMessage: 'Home', + })} + + ), + }); + + // Content + navItems.push({ + 'data-test-subj': 'searchSideNav-Content', + id: 'content', + items: [ + { + 'data-test-subj': 'searchSideNav-Indices', + deepLink: { + link: 'enterpriseSearchContent:searchIndices', + shouldShowActiveForSubroutes: true, + }, + id: 'search_indices', + }, + { + 'data-test-subj': 'searchSideNav-Connectors', + deepLink: { + link: 'enterpriseSearchContent:connectors', + shouldShowActiveForSubroutes: true, + }, + id: 'connectors', + }, + { + 'data-test-subj': 'searchSideNav-Crawlers', + deepLink: { + link: 'enterpriseSearchContent:webCrawlers', + shouldShowActiveForSubroutes: true, + }, + id: 'crawlers', + }, + ], + name: i18n.translate('xpack.enterpriseSearch.nav.contentTitle', { + defaultMessage: 'Content', + }), + }); + + // Build + navItems.push({ + 'data-test-subj': 'searchSideNav-Build', + id: 'build', + items: [ + { + 'data-test-subj': 'searchSideNav-Playground', + deepLink: { + link: 'enterpriseSearchApplications:playground', + shouldShowActiveForSubroutes: true, + }, + id: 'playground', + }, + { + 'data-test-subj': 'searchSideNav-SearchApplications', + deepLink: { + link: 'enterpriseSearchApplications:searchApplications', + }, + id: 'searchApplications', + }, + { + 'data-test-subj': 'searchSideNav-BehavioralAnalytics', + deepLink: { + link: ENTERPRISE_SEARCH_ANALYTICS_APP_ID, + }, + id: 'analyticsCollections', + }, + ], + name: i18n.translate('xpack.enterpriseSearch.nav.applicationsTitle', { + defaultMessage: 'Build', + }), + }); + + navItems.push({ + 'data-test-subj': 'searchSideNav-Relevance', + id: 'relevance', + items: [ + { + 'data-test-subj': 'searchSideNav-InferenceEndpoints', + deepLink: { + link: 'searchInferenceEndpoints:inferenceEndpoints', + shouldShowActiveForSubroutes: true, + }, + id: 'inference_endpoints', + }, + ], + name: i18n.translate('xpack.enterpriseSearch.nav.relevanceTitle', { + defaultMessage: 'Relevance', + }), + }); + + // Getting Started + navItems.push({ + 'data-test-subj': 'searchSideNav-GettingStarted', + id: 'es_getting_started', + items: [ + { + 'data-test-subj': 'searchSideNav-Elasticsearch', + deepLink: { + link: SEARCH_ELASTICSEARCH, + }, + id: 'elasticsearch', + }, + { + 'data-test-subj': 'searchSideNav-VectorSearch', + deepLink: { + link: SEARCH_VECTOR_SEARCH, + }, + id: 'vectorSearch', + }, + { + 'data-test-subj': 'searchSideNav-SemanticSearch', + deepLink: { + link: SEARCH_SEMANTIC_SEARCH, + }, + id: 'semanticSearch', + }, + { + 'data-test-subj': 'searchSideNav-AISearch', + deepLink: { + link: SEARCH_AI_SEARCH, + }, + id: 'aiSearch', + }, + ], + name: GETTING_STARTED_TITLE, + }); + + if (productAccess.hasAppSearchAccess || productAccess.hasWorkplaceSearchAccess) { + const entSearchItems: ClassicNavItem[] = []; + if (productAccess.hasAppSearchAccess) { + entSearchItems.push({ + 'data-test-subj': 'searchSideNav-AppSearch', + deepLink: { + link: 'appSearch:engines', + }, + id: 'app_search', + }); + } + if (productAccess.hasWorkplaceSearchAccess) { + entSearchItems.push({ + 'data-test-subj': 'searchSideNav-WorkplaceSearch', + deepLink: { + link: 'workplaceSearch', + }, + id: 'workplace_search', + }); + } + navItems.push({ + 'data-test-subj': 'searchSideNav-EnterpriseSearch', + id: 'enterpriseSearch', + items: entSearchItems, + name: i18n.translate('xpack.enterpriseSearch.nav.title', { + defaultMessage: 'Enterprise Search', + }), + }); + } + + return navItems; +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/classic_nav_helpers.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/layout/classic_nav_helpers.test.ts new file mode 100644 index 0000000000000..514072ba297aa --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/classic_nav_helpers.test.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 { mockKibanaValues } from '../../__mocks__/kea_logic'; + +import type { ChromeNavLink } from '@kbn/core-chrome-browser'; + +import '../../__mocks__/react_router'; + +jest.mock('../react_router_helpers/link_events', () => ({ + letBrowserHandleEvent: jest.fn(), +})); + +import { ClassicNavItem } from '../types'; + +import { generateSideNavItems } from './classic_nav_helpers'; + +describe('generateSideNavItems', () => { + const deepLinksMap = { + enterpriseSearch: { + id: 'enterpriseSearch', + url: '/app/enterprise_search/overview', + title: 'Overview', + }, + 'enterpriseSearchContent:searchIndices': { + id: 'enterpriseSearchContent:searchIndices', + title: 'Indices', + url: '/app/enterprise_search/content/search_indices', + }, + 'enterpriseSearchContent:connectors': { + id: 'enterpriseSearchContent:connectors', + title: 'Connectors', + url: '/app/enterprise_search/content/connectors', + }, + 'enterpriseSearchContent:webCrawlers': { + id: 'enterpriseSearchContent:webCrawlers', + title: 'Web crawlers', + url: '/app/enterprise_search/content/crawlers', + }, + } as unknown as Record; + beforeEach(() => { + jest.clearAllMocks(); + mockKibanaValues.history.location.pathname = '/'; + }); + + it('renders top-level items', () => { + const classicNavItems: ClassicNavItem[] = [ + { + id: 'unit-test', + deepLink: { + link: 'enterpriseSearch', + }, + }, + ]; + + expect(generateSideNavItems(classicNavItems, deepLinksMap)).toEqual([ + { + href: '/app/enterprise_search/overview', + id: 'unit-test', + isSelected: false, + name: 'Overview', + onClick: expect.any(Function), + }, + ]); + }); + + it('renders items with children', () => { + const classicNavItems: ClassicNavItem[] = [ + { + id: 'parent', + name: 'Parent', + items: [ + { + id: 'unit-test', + deepLink: { + link: 'enterpriseSearch', + }, + }, + ], + }, + ]; + + expect(generateSideNavItems(classicNavItems, deepLinksMap)).toEqual([ + { + id: 'parent', + items: [ + { + href: '/app/enterprise_search/overview', + id: 'unit-test', + isSelected: false, + name: 'Overview', + onClick: expect.any(Function), + }, + ], + name: 'Parent', + }, + ]); + }); + + it('renders classic nav name over deep link title if provided', () => { + const classicNavItems: ClassicNavItem[] = [ + { + deepLink: { + link: 'enterpriseSearch', + }, + id: 'unit-test', + name: 'Home', + }, + ]; + + expect(generateSideNavItems(classicNavItems, deepLinksMap)).toEqual([ + { + href: '/app/enterprise_search/overview', + id: 'unit-test', + isSelected: false, + name: 'Home', + onClick: expect.any(Function), + }, + ]); + }); + + it('removes item if deep link is not defined', () => { + const classicNavItems: ClassicNavItem[] = [ + { + deepLink: { + link: 'enterpriseSearch', + }, + id: 'unit-test', + name: 'Home', + }, + { + deepLink: { + link: 'enterpriseSearchApplications:playground', + }, + id: 'unit-test-missing', + }, + ]; + + expect(generateSideNavItems(classicNavItems, deepLinksMap)).toEqual([ + { + href: '/app/enterprise_search/overview', + id: 'unit-test', + isSelected: false, + name: 'Home', + onClick: expect.any(Function), + }, + ]); + }); + + it('adds pre-rendered child items provided', () => { + const classicNavItems: ClassicNavItem[] = [ + { + id: 'unit-test', + name: 'Indices', + }, + ]; + const subItems = { + 'unit-test': [ + { + href: '/app/unit-test', + id: 'child', + isSelected: true, + name: 'Index', + onClick: jest.fn(), + }, + ], + }; + + expect(generateSideNavItems(classicNavItems, deepLinksMap, subItems)).toEqual([ + { + id: 'unit-test', + items: [ + { + href: '/app/unit-test', + id: 'child', + isSelected: true, + name: 'Index', + onClick: expect.any(Function), + }, + ], + name: 'Indices', + }, + ]); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/classic_nav_helpers.ts b/x-pack/plugins/enterprise_search/public/applications/shared/layout/classic_nav_helpers.ts new file mode 100644 index 0000000000000..89f3c2ab5b59a --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/classic_nav_helpers.ts @@ -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 { ChromeNavLink, EuiSideNavItemTypeEnhanced } from '@kbn/core-chrome-browser'; + +import { + ClassicNavItem, + GenerateNavLinkFromDeepLinkParameters, + GenerateNavLinkParameters, +} from '../types'; + +import { generateNavLink } from './nav_link_helpers'; + +export const generateSideNavItems = ( + navItems: ClassicNavItem[], + deepLinks: Record, + subItemsMap: Record> | undefined> = {} +): Array> => { + const sideNavItems: Array> = []; + + for (const navItem of navItems) { + let sideNavChildItems: Array> | undefined; + + const { deepLink, items, ...rest } = navItem; + const subItems = subItemsMap?.[navItem.id]; + + if (items || subItems) { + sideNavChildItems = []; + if (items) { + sideNavChildItems.push(...generateSideNavItems(items, deepLinks, subItemsMap)); + } + if (subItems) { + sideNavChildItems.push(...subItems); + } + } + + let sideNavItem: EuiSideNavItemTypeEnhanced | undefined; + if (deepLink) { + const navLinkParams = getNavLinkParameters(deepLink, deepLinks); + if (navLinkParams !== undefined) { + const name = navItem.name ?? getDeepLinkTitle(deepLink.link, deepLinks); + sideNavItem = { + ...rest, + name, + ...generateNavLink({ + ...navLinkParams, + items: sideNavChildItems, + }), + }; + } + } else { + sideNavItem = { + ...rest, + items: sideNavChildItems, + name: navItem.name, + }; + } + + if (isValidSideNavItem(sideNavItem)) { + sideNavItems.push(sideNavItem); + } + } + + return sideNavItems; +}; + +const getNavLinkParameters = ( + navLink: GenerateNavLinkFromDeepLinkParameters, + deepLinks: Record +): GenerateNavLinkParameters | undefined => { + const { link, ...navLinkProps } = navLink; + const deepLink = deepLinks[link]; + if (!deepLink || !deepLink.url) return undefined; + return { + ...navLinkProps, + shouldNotCreateHref: true, + shouldNotPrepend: true, + to: deepLink.url, + }; +}; +const getDeepLinkTitle = ( + link: string, + deepLinks: Record +): string | undefined => { + const deepLink = deepLinks[link]; + if (!deepLink || !deepLink.url) return undefined; + return deepLink.title; +}; + +function isValidSideNavItem( + item: EuiSideNavItemTypeEnhanced | undefined +): item is EuiSideNavItemTypeEnhanced { + if (item === undefined) return false; + if (item.href || item.onClick) return true; + if (item?.items?.length ?? 0 > 0) return true; + + return false; +} 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 b2c31ff4868bc..3305e92dd8d9e 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 @@ -15,6 +15,8 @@ jest.mock('../../enterprise_search_content/components/search_index/indices/indic import { setMockValues, mockKibanaValues } from '../../__mocks__/kea_logic'; +import { renderHook } from '@testing-library/react-hooks'; + import { EuiSideNavItemType } from '@elastic/eui'; import { DEFAULT_PRODUCT_FEATURES } from '../../../../common/constants'; @@ -32,26 +34,31 @@ const DEFAULT_PRODUCT_ACCESS: ProductAccess = { }; const baseNavItems = [ expect.objectContaining({ + 'data-test-subj': 'searchSideNav-Home', href: '/app/enterprise_search/overview', id: 'home', items: undefined, }), { + 'data-test-subj': 'searchSideNav-Content', id: 'content', items: [ { + 'data-test-subj': 'searchSideNav-Indices', href: '/app/enterprise_search/content/search_indices', id: 'search_indices', items: [], name: 'Indices', }, { + 'data-test-subj': 'searchSideNav-Connectors', href: '/app/enterprise_search/content/connectors', id: 'connectors', items: undefined, name: 'Connectors', }, { + 'data-test-subj': 'searchSideNav-Crawlers', href: '/app/enterprise_search/content/crawlers', id: 'crawlers', items: undefined, @@ -61,21 +68,25 @@ const baseNavItems = [ name: 'Content', }, { + 'data-test-subj': 'searchSideNav-Build', id: 'build', items: [ { + 'data-test-subj': 'searchSideNav-Playground', href: '/app/enterprise_search/applications/playground', id: 'playground', items: undefined, name: 'Playground', }, { + 'data-test-subj': 'searchSideNav-SearchApplications', href: '/app/enterprise_search/applications/search_applications', id: 'searchApplications', items: undefined, name: 'Search Applications', }, { + 'data-test-subj': 'searchSideNav-BehavioralAnalytics', href: '/app/enterprise_search/analytics', id: 'analyticsCollections', items: undefined, @@ -85,9 +96,11 @@ const baseNavItems = [ name: 'Build', }, { + 'data-test-subj': 'searchSideNav-Relevance', id: 'relevance', items: [ { + 'data-test-subj': 'searchSideNav-InferenceEndpoints', href: '/app/enterprise_search/relevance/inference_endpoints', id: 'inference_endpoints', items: undefined, @@ -97,27 +110,32 @@ const baseNavItems = [ name: 'Relevance', }, { + 'data-test-subj': 'searchSideNav-GettingStarted', id: 'es_getting_started', items: [ { + 'data-test-subj': 'searchSideNav-Elasticsearch', href: '/app/enterprise_search/elasticsearch', id: 'elasticsearch', items: undefined, name: 'Elasticsearch', }, { + 'data-test-subj': 'searchSideNav-VectorSearch', href: '/app/enterprise_search/vector_search', id: 'vectorSearch', items: undefined, name: 'Vector Search', }, { + 'data-test-subj': 'searchSideNav-SemanticSearch', href: '/app/enterprise_search/semantic_search', id: 'semanticSearch', items: undefined, name: 'Semantic Search', }, { + 'data-test-subj': 'searchSideNav-AISearch', href: '/app/enterprise_search/ai_search', id: 'aiSearch', items: undefined, @@ -127,15 +145,18 @@ const baseNavItems = [ name: 'Getting started', }, { + 'data-test-subj': 'searchSideNav-EnterpriseSearch', id: 'enterpriseSearch', items: [ { + 'data-test-subj': 'searchSideNav-AppSearch', href: '/app/enterprise_search/app_search', id: 'app_search', items: undefined, name: 'App Search', }, { + 'data-test-subj': 'searchSideNav-WorkplaceSearch', href: '/app/enterprise_search/workplace_search', id: 'workplace_search', items: undefined, @@ -146,21 +167,102 @@ const baseNavItems = [ }, ]; +const mockNavLinks = [ + { + id: 'enterpriseSearch', + url: '/app/enterprise_search/overview', + }, + { + id: 'enterpriseSearchContent:searchIndices', + title: 'Indices', + url: '/app/enterprise_search/content/search_indices', + }, + { + id: 'enterpriseSearchContent:connectors', + title: 'Connectors', + url: '/app/enterprise_search/content/connectors', + }, + { + id: 'enterpriseSearchContent:webCrawlers', + title: 'Web crawlers', + url: '/app/enterprise_search/content/crawlers', + }, + { + id: 'enterpriseSearchApplications:playground', + title: 'Playground', + url: '/app/enterprise_search/applications/playground', + }, + { + id: 'enterpriseSearchApplications:searchApplications', + title: 'Search Applications', + url: '/app/enterprise_search/applications/search_applications', + }, + { + id: 'enterpriseSearchAnalytics', + title: 'Behavioral Analytics', + url: '/app/enterprise_search/analytics', + }, + { + id: 'searchInferenceEndpoints:inferenceEndpoints', + title: 'Inference Endpoints', + url: '/app/enterprise_search/relevance/inference_endpoints', + }, + { + id: 'appSearch:engines', + title: 'App Search', + url: '/app/enterprise_search/app_search', + }, + { + id: 'workplaceSearch', + title: 'Workplace Search', + url: '/app/enterprise_search/workplace_search', + }, + { + id: 'enterpriseSearchElasticsearch', + title: 'Elasticsearch', + url: '/app/enterprise_search/elasticsearch', + }, + { + id: 'enterpriseSearchVectorSearch', + title: 'Vector Search', + url: '/app/enterprise_search/vector_search', + }, + { + id: 'enterpriseSearchSemanticSearch', + title: 'Semantic Search', + url: '/app/enterprise_search/semantic_search', + }, + { + id: 'enterpriseSearchAISearch', + title: 'AI Search', + url: '/app/enterprise_search/ai_search', + }, +]; + +const defaultMockValues = { + hasEnterpriseLicense: true, + isSidebarEnabled: true, + productAccess: DEFAULT_PRODUCT_ACCESS, + productFeatures: DEFAULT_PRODUCT_FEATURES, +}; + describe('useEnterpriseSearchContentNav', () => { beforeEach(() => { jest.clearAllMocks(); mockKibanaValues.uiSettings.get.mockReturnValue(false); + mockKibanaValues.getNavLinks.mockReturnValue(mockNavLinks); }); it('returns an array of top-level Enterprise Search nav items', () => { const fullProductAccess: ProductAccess = DEFAULT_PRODUCT_ACCESS; setMockValues({ - isSidebarEnabled: true, + ...defaultMockValues, productAccess: fullProductAccess, - productFeatures: DEFAULT_PRODUCT_FEATURES, }); - expect(useEnterpriseSearchNav()).toEqual(baseNavItems); + const { result } = renderHook(() => useEnterpriseSearchNav()); + + expect(result.current).toEqual(baseNavItems); }); it('excludes legacy products when the user has no access to them', () => { @@ -171,13 +273,13 @@ describe('useEnterpriseSearchContentNav', () => { }; setMockValues({ - isSidebarEnabled: true, + ...defaultMockValues, productAccess: noProductAccess, - productFeatures: DEFAULT_PRODUCT_FEATURES, }); mockKibanaValues.uiSettings.get.mockReturnValue(false); - const esNav = useEnterpriseSearchNav(); + const { result } = renderHook(() => useEnterpriseSearchNav()); + const esNav = result.current; const legacyESNav = esNav?.find((item) => item.id === 'enterpriseSearch'); expect(legacyESNav).toBeUndefined(); }); @@ -190,18 +292,20 @@ describe('useEnterpriseSearchContentNav', () => { }; setMockValues({ - isSidebarEnabled: true, + ...defaultMockValues, productAccess: workplaceSearchProductAccess, - productFeatures: DEFAULT_PRODUCT_FEATURES, }); - const esNav = useEnterpriseSearchNav(); + const { result } = renderHook(() => useEnterpriseSearchNav()); + const esNav = result.current; const legacyESNav = esNav?.find((item) => item.id === 'enterpriseSearch'); expect(legacyESNav).not.toBeUndefined(); expect(legacyESNav).toEqual({ + 'data-test-subj': 'searchSideNav-EnterpriseSearch', id: 'enterpriseSearch', items: [ { + 'data-test-subj': 'searchSideNav-WorkplaceSearch', href: '/app/enterprise_search/workplace_search', id: 'workplace_search', name: 'Workplace Search', @@ -218,18 +322,20 @@ describe('useEnterpriseSearchContentNav', () => { }; setMockValues({ - isSidebarEnabled: true, + ...defaultMockValues, productAccess: appSearchProductAccess, - productFeatures: DEFAULT_PRODUCT_FEATURES, }); - const esNav = useEnterpriseSearchNav(); + const { result } = renderHook(() => useEnterpriseSearchNav()); + const esNav = result.current; const legacyESNav = esNav?.find((item) => item.id === 'enterpriseSearch'); expect(legacyESNav).not.toBeUndefined(); expect(legacyESNav).toEqual({ + 'data-test-subj': 'searchSideNav-EnterpriseSearch', id: 'enterpriseSearch', items: [ { + 'data-test-subj': 'searchSideNav-AppSearch', href: '/app/enterprise_search/app_search', id: 'app_search', name: 'App Search', @@ -243,21 +349,21 @@ describe('useEnterpriseSearchContentNav', () => { describe('useEnterpriseSearchApplicationNav', () => { beforeEach(() => { jest.clearAllMocks(); + mockKibanaValues.getNavLinks.mockReturnValue(mockNavLinks); mockKibanaValues.uiSettings.get.mockReturnValue(true); - setMockValues({ - isSidebarEnabled: true, - productAccess: DEFAULT_PRODUCT_ACCESS, - productFeatures: DEFAULT_PRODUCT_FEATURES, - }); + setMockValues(defaultMockValues); }); it('returns an array of top-level Enterprise Search nav items', () => { - expect(useEnterpriseSearchApplicationNav()).toEqual(baseNavItems); + const { result } = renderHook(() => useEnterpriseSearchApplicationNav()); + expect(result.current).toEqual(baseNavItems); }); it('returns selected engine sub nav items', () => { const engineName = 'my-test-engine'; - const navItems = useEnterpriseSearchApplicationNav(engineName); + const { + result: { current: navItems }, + } = renderHook(() => useEnterpriseSearchApplicationNav(engineName)); expect(navItems![0].id).toEqual('home'); expect(navItems?.slice(1).map((ni) => ni.name)).toEqual([ 'Content', @@ -317,7 +423,9 @@ describe('useEnterpriseSearchApplicationNav', () => { it('returns selected engine without tabs when isEmpty', () => { const engineName = 'my-test-engine'; - const navItems = useEnterpriseSearchApplicationNav(engineName, true); + const { + result: { current: navItems }, + } = renderHook(() => useEnterpriseSearchApplicationNav(engineName, true)); expect(navItems![0].id).toEqual('home'); expect(navItems?.slice(1).map((ni) => ni.name)).toEqual([ 'Content', @@ -348,7 +456,9 @@ describe('useEnterpriseSearchApplicationNav', () => { it('returns selected engine with conflict warning when hasSchemaConflicts', () => { const engineName = 'my-test-engine'; - const navItems = useEnterpriseSearchApplicationNav(engineName, false, true); + const { + result: { current: navItems }, + } = renderHook(() => useEnterpriseSearchApplicationNav(engineName, false, true)); // @ts-ignore const engineItem = navItems @@ -383,27 +493,20 @@ describe('useEnterpriseSearchApplicationNav', () => { describe('useEnterpriseSearchAnalyticsNav', () => { beforeEach(() => { jest.clearAllMocks(); - setMockValues({ - isSidebarEnabled: true, - }); + setMockValues(defaultMockValues); + mockKibanaValues.getNavLinks.mockReturnValue(mockNavLinks); }); it('returns basic nav all params are empty', () => { - const navItems = useEnterpriseSearchAnalyticsNav(); - expect(navItems).toEqual( - baseNavItems.map((item) => - item.id === 'content' - ? { - ...item, - items: item.items, - } - : item - ) - ); + const { result } = renderHook(() => useEnterpriseSearchAnalyticsNav()); + + expect(result.current).toEqual(baseNavItems); }); it('returns basic nav if only name provided', () => { - const navItems = useEnterpriseSearchAnalyticsNav('my-test-collection'); + const { + result: { current: navItems }, + } = renderHook(() => useEnterpriseSearchAnalyticsNav('my-test-collection')); expect(navItems).toEqual( baseNavItems.map((item) => item.id === 'content' @@ -417,16 +520,21 @@ describe('useEnterpriseSearchAnalyticsNav', () => { }); it('returns nav with sub items when name and paths provided', () => { - const navItems = useEnterpriseSearchAnalyticsNav('my-test-collection', { - explorer: '/explorer-path', - integration: '/integration-path', - overview: '/overview-path', - }); + const { + result: { current: navItems }, + } = renderHook(() => + useEnterpriseSearchAnalyticsNav('my-test-collection', { + explorer: '/explorer-path', + integration: '/integration-path', + overview: '/overview-path', + }) + ); const applicationsNav = navItems?.find((item) => item.id === 'build'); expect(applicationsNav).not.toBeUndefined(); const analyticsNav = applicationsNav?.items?.[2]; expect(analyticsNav).not.toBeUndefined(); expect(analyticsNav).toEqual({ + 'data-test-subj': 'searchSideNav-BehavioralAnalytics', href: '/app/enterprise_search/analytics', id: 'analyticsCollections', items: [ 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 3b3960a7a92ba..8f83b6c73402e 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 @@ -5,44 +5,22 @@ * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { useValues } from 'kea'; -import { EuiFlexGroup, EuiIcon, EuiText } from '@elastic/eui'; -import type { EuiSideNavItemTypeEnhanced } from '@kbn/core-chrome-browser'; +import { EuiFlexGroup, EuiIcon } from '@elastic/eui'; +import type { ChromeNavLink, EuiSideNavItemTypeEnhanced } from '@kbn/core-chrome-browser'; import { i18n } from '@kbn/i18n'; -import { - ANALYTICS_PLUGIN, - APPLICATIONS_PLUGIN, - APP_SEARCH_PLUGIN, - ELASTICSEARCH_PLUGIN, - ENTERPRISE_SEARCH_CONTENT_PLUGIN, - ENTERPRISE_SEARCH_OVERVIEW_PLUGIN, - AI_SEARCH_PLUGIN, - VECTOR_SEARCH_PLUGIN, - WORKPLACE_SEARCH_PLUGIN, - SEARCH_RELEVANCE_PLUGIN, - SEMANTIC_SEARCH_PLUGIN, -} from '../../../../common/constants'; -import { - SEARCH_APPLICATIONS_PATH, - SearchApplicationViewTabs, - PLAYGROUND_PATH, -} from '../../applications/routes'; +import { ANALYTICS_PLUGIN, APPLICATIONS_PLUGIN } from '../../../../common/constants'; +import { SEARCH_APPLICATIONS_PATH, SearchApplicationViewTabs } from '../../applications/routes'; import { useIndicesNav } from '../../enterprise_search_content/components/search_index/indices/indices_nav'; -import { - CONNECTORS_PATH, - CRAWLERS_PATH, - SEARCH_INDICES_PATH, -} from '../../enterprise_search_content/routes'; -import { INFERENCE_ENDPOINTS_PATH } from '../../enterprise_search_relevance/routes'; import { KibanaLogic } from '../kibana'; -import { LicensingLogic } from '../licensing'; - +import { buildBaseClassicNavItems } from './base_nav'; +import { generateSideNavItems } from './classic_nav_helpers'; import { generateNavLink } from './nav_link_helpers'; /** @@ -52,219 +30,21 @@ import { generateNavLink } from './nav_link_helpers'; * @returns The Enterprise Search navigation items */ export const useEnterpriseSearchNav = (alwaysReturn = false) => { - const { isSidebarEnabled, productAccess } = useValues(KibanaLogic); - - const { hasEnterpriseLicense } = useValues(LicensingLogic); + const { isSidebarEnabled, productAccess, getNavLinks } = useValues(KibanaLogic); const indicesNavItems = useIndicesNav(); - if (!isSidebarEnabled && !alwaysReturn) return undefined; + const navItems: Array> = useMemo(() => { + const baseNavItems = buildBaseClassicNavItems({ productAccess }); + const deepLinks = getNavLinks().reduce((links, link) => { + links[link.id] = link; + return links; + }, {} as Record); - 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: [ - { - id: 'search_indices', - name: i18n.translate('xpack.enterpriseSearch.nav.searchIndicesTitle', { - defaultMessage: 'Indices', - }), - ...generateNavLink({ - items: indicesNavItems, - shouldNotCreateHref: true, - shouldShowActiveForSubroutes: true, - to: ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL + SEARCH_INDICES_PATH, - }), - }, - { - id: 'connectors', - name: i18n.translate('xpack.enterpriseSearch.nav.connectorsTitle', { - defaultMessage: 'Connectors', - }), - ...generateNavLink({ - shouldNotCreateHref: true, - shouldShowActiveForSubroutes: true, - to: ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL + CONNECTORS_PATH, - }), - }, - { - id: 'crawlers', - name: i18n.translate('xpack.enterpriseSearch.nav.crawlersTitle', { - defaultMessage: 'Web crawlers', - }), - ...generateNavLink({ - shouldNotCreateHref: true, - shouldShowActiveForSubroutes: true, - to: ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL + CRAWLERS_PATH, - }), - }, - ], - name: i18n.translate('xpack.enterpriseSearch.nav.contentTitle', { - defaultMessage: 'Content', - }), - }, - { - id: 'build', - items: [ - { - id: 'playground', - name: i18n.translate('xpack.enterpriseSearch.nav.PlaygroundTitle', { - defaultMessage: 'Playground', - }), - ...generateNavLink({ - shouldNotCreateHref: true, - shouldShowActiveForSubroutes: true, - to: APPLICATIONS_PLUGIN.URL + PLAYGROUND_PATH, - }), - }, - { - id: 'searchApplications', - name: i18n.translate('xpack.enterpriseSearch.nav.searchApplicationsTitle', { - defaultMessage: 'Search Applications', - }), - ...generateNavLink({ - shouldNotCreateHref: true, - to: APPLICATIONS_PLUGIN.URL + SEARCH_APPLICATIONS_PATH, - }), - }, - { - id: 'analyticsCollections', - name: i18n.translate('xpack.enterpriseSearch.nav.analyticsTitle', { - defaultMessage: 'Behavioral Analytics', - }), - ...generateNavLink({ - shouldNotCreateHref: true, - to: ANALYTICS_PLUGIN.URL, - }), - }, - ], - name: i18n.translate('xpack.enterpriseSearch.nav.applicationsTitle', { - defaultMessage: 'Build', - }), - }, - ...(hasEnterpriseLicense - ? [ - { - id: 'relevance', - items: [ - { - id: 'inference_endpoints', - name: i18n.translate('xpack.enterpriseSearch.nav.inferenceEndpointsTitle', { - defaultMessage: 'Inference Endpoints', - }), - ...generateNavLink({ - shouldNotCreateHref: true, - shouldShowActiveForSubroutes: true, - to: SEARCH_RELEVANCE_PLUGIN.URL + INFERENCE_ENDPOINTS_PATH, - }), - }, - ], - name: i18n.translate('xpack.enterpriseSearch.nav.relevanceTitle', { - defaultMessage: 'Relevance', - }), - }, - ] - : []), - { - id: 'es_getting_started', - items: [ - { - id: 'elasticsearch', - name: i18n.translate('xpack.enterpriseSearch.nav.elasticsearchTitle', { - defaultMessage: 'Elasticsearch', - }), - ...generateNavLink({ - shouldNotCreateHref: true, - to: ELASTICSEARCH_PLUGIN.URL, - }), - }, - { - id: 'vectorSearch', - name: VECTOR_SEARCH_PLUGIN.NAME, - ...generateNavLink({ - shouldNotCreateHref: true, - to: VECTOR_SEARCH_PLUGIN.URL, - }), - }, - { - id: 'semanticSearch', - name: SEMANTIC_SEARCH_PLUGIN.NAME, - ...generateNavLink({ - shouldNotCreateHref: true, - to: SEMANTIC_SEARCH_PLUGIN.URL, - }), - }, - { - id: 'aiSearch', - name: i18n.translate('xpack.enterpriseSearch.nav.aiSearchTitle', { - defaultMessage: 'AI Search', - }), - ...generateNavLink({ - shouldNotCreateHref: true, - to: AI_SEARCH_PLUGIN.URL, - }), - }, - ], - name: i18n.translate('xpack.enterpriseSearch.nav.enterpriseSearchOverviewTitle', { - defaultMessage: 'Getting started', - }), - }, - ...(productAccess.hasAppSearchAccess || productAccess.hasWorkplaceSearchAccess - ? [ - { - id: 'enterpriseSearch', - items: [ - ...(productAccess.hasAppSearchAccess - ? [ - { - id: 'app_search', - name: i18n.translate('xpack.enterpriseSearch.nav.appSearchTitle', { - defaultMessage: 'App Search', - }), - ...generateNavLink({ - shouldNotCreateHref: true, - to: APP_SEARCH_PLUGIN.URL, - }), - }, - ] - : []), - ...(productAccess.hasWorkplaceSearchAccess - ? [ - { - id: 'workplace_search', - name: i18n.translate('xpack.enterpriseSearch.nav.workplaceSearchTitle', { - defaultMessage: 'Workplace Search', - }), - ...generateNavLink({ - shouldNotCreateHref: true, - to: WORKPLACE_SEARCH_PLUGIN.URL, - }), - }, - ] - : []), - ], - name: i18n.translate('xpack.enterpriseSearch.nav.title', { - defaultMessage: 'Enterprise Search', - }), - }, - ] - : []), - ]; + return generateSideNavItems(baseNavItems, deepLinks, { search_indices: indicesNavItems }); + }, [productAccess, indicesNavItems]); + + if (!isSidebarEnabled && !alwaysReturn) return undefined; return navItems; }; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav_link_helpers.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav_link_helpers.test.ts index fff28345bb1bb..50c85a268e366 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav_link_helpers.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav_link_helpers.test.ts @@ -36,6 +36,7 @@ describe('generateNavLink', () => { navItem.onClick({ preventDefault: jest.fn() } as any); expect(mockKibanaValues.navigateToUrl).toHaveBeenCalledWith('/test', { shouldNotCreateHref: false, + shouldNotPrepend: false, }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav_link_helpers.ts b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav_link_helpers.ts index f086433c9fc0e..36000307adcc3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav_link_helpers.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav_link_helpers.ts @@ -5,27 +5,32 @@ * 2.0. */ -import { EuiSideNavItemType } from '@elastic/eui'; +import { EuiSideNavItemTypeEnhanced } from '@kbn/core-chrome-browser'; import { stripTrailingSlash } from '../../../../common/strip_slashes'; import { KibanaLogic } from '../kibana'; -import { generateReactRouterProps, ReactRouterProps } from '../react_router_helpers'; -import { GeneratedReactRouterProps } from '../react_router_helpers/generate_react_router_props'; +import { + type GeneratedReactRouterProps, + generateReactRouterProps, +} from '../react_router_helpers/generate_react_router_props'; +import { ReactRouterProps } from '../types'; interface Params { - items?: Array>; // Primarily passed if using `items` to determine isSelected - if not, you can just set `items` outside of this helper + items?: Array>; // Primarily passed if using `items` to determine isSelected - if not, you can just set `items` outside of this helper shouldShowActiveForSubroutes?: boolean; to: string; } type NavLinkProps = GeneratedReactRouterProps & - Pick, 'isSelected' | 'items'>; + Pick, 'isSelected' | 'items'>; + +export type GenerateNavLinkParameters = Params & ReactRouterProps; export const generateNavLink = ({ items, ...rest -}: Params & ReactRouterProps): NavLinkProps => { +}: GenerateNavLinkParameters): NavLinkProps => { const linkProps = { ...generateReactRouterProps({ ...rest }), isSelected: getNavLinkActive({ items, ...rest }), @@ -38,14 +43,15 @@ export const getNavLinkActive = ({ shouldShowActiveForSubroutes = false, items = [], shouldNotCreateHref = false, -}: Params & ReactRouterProps): boolean => { + shouldNotPrepend = false, +}: GenerateNavLinkParameters): boolean => { const { pathname } = KibanaLogic.values.history.location; const currentPath = stripTrailingSlash(pathname); const { href: currentPathHref } = generateReactRouterProps({ shouldNotCreateHref: false, to: currentPath, }); - const { href: toHref } = generateReactRouterProps({ shouldNotCreateHref, to }); + const { href: toHref } = generateReactRouterProps({ shouldNotCreateHref, shouldNotPrepend, to }); if (currentPathHref === toHref) return true; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/create_href.ts b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/create_href.ts index a399d632140b6..cf02c3ed74f71 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/create_href.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/create_href.ts @@ -30,12 +30,19 @@ interface CreateHrefDeps { } export interface CreateHrefOptions { shouldNotCreateHref?: boolean; + shouldNotPrepend?: boolean; } export const createHref = ( path: string, { history, http }: CreateHrefDeps, - { shouldNotCreateHref }: CreateHrefOptions = {} + { shouldNotCreateHref, shouldNotPrepend }: CreateHrefOptions = {} ): string => { - return shouldNotCreateHref ? http.basePath.prepend(path) : history.createHref({ pathname: path }); + if (shouldNotCreateHref) { + if (shouldNotPrepend) { + return path; + } + return http.basePath.prepend(path); + } + return history.createHref({ pathname: path }); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_components.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_components.tsx index 8271f49f9f39a..708cc597e582d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_components.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_components.tsx @@ -26,7 +26,9 @@ import { } from '@elastic/eui'; import { EuiPanelProps } from '@elastic/eui/src/components/panel/panel'; -import { generateReactRouterProps, ReactRouterProps } from '.'; +import { ReactRouterProps } from '../types'; + +import { generateReactRouterProps } from '.'; /** * Correctly typed component helpers with React-Router-friendly `href` and `onClick` props diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/generate_react_router_props.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/generate_react_router_props.test.ts index 309f94fcf55b4..de2a80ee5eaf4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/generate_react_router_props.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/generate_react_router_props.test.ts @@ -44,6 +44,7 @@ describe('generateReactRouterProps', () => { expect(mockEvent.preventDefault).toHaveBeenCalled(); expect(mockKibanaValues.navigateToUrl).toHaveBeenCalledWith('/test', { shouldNotCreateHref: false, + shouldNotPrepend: false, }); }); @@ -63,6 +64,7 @@ describe('generateReactRouterProps', () => { expect(mockEvent.preventDefault).toHaveBeenCalled(); expect(mockKibanaValues.navigateToUrl).toHaveBeenCalledWith('/app/enterprise_search/test', { shouldNotCreateHref: true, + shouldNotPrepend: false, }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/generate_react_router_props.ts b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/generate_react_router_props.ts index 2ef7f556eb2d1..89219362e5be4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/generate_react_router_props.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/generate_react_router_props.ts @@ -11,6 +11,7 @@ import { EuiSideNavItemType } from '@elastic/eui'; import { HttpLogic } from '../http'; import { KibanaLogic } from '../kibana'; +import { ReactRouterProps } from '../types'; import { letBrowserHandleEvent, createHref } from '.'; @@ -23,14 +24,6 @@ import { letBrowserHandleEvent, createHref } from '.'; * but separated out from EuiLink portion as we use this for multiple EUI components */ -export interface ReactRouterProps { - to: string; - onClick?(): void; - // Used to navigate outside of the React Router plugin basename but still within Kibana, - // e.g. if we need to go from Enterprise Search to App Search - shouldNotCreateHref?: boolean; -} - export type GeneratedReactRouterProps = Required< Pick, 'href' | 'onClick'> >; @@ -39,12 +32,13 @@ export const generateReactRouterProps = ({ to, onClick, shouldNotCreateHref = false, + shouldNotPrepend = false, }: ReactRouterProps): GeneratedReactRouterProps => { const { navigateToUrl, history } = KibanaLogic.values; const { http } = HttpLogic.values; // Generate the correct link href (with basename etc. accounted for) - const href = createHref(to, { history, http }, { shouldNotCreateHref }); + const href = createHref(to, { history, http }, { shouldNotCreateHref, shouldNotPrepend }); const reactRouterLinkClick = (event: React.MouseEvent) => { if (onClick) onClick(); // Run any passed click events (e.g. telemetry) @@ -54,7 +48,7 @@ export const generateReactRouterProps = ({ event.preventDefault(); // Perform SPA navigation. - navigateToUrl(to, { shouldNotCreateHref }); + navigateToUrl(to, { shouldNotCreateHref, shouldNotPrepend }); }; return { href, onClick: reactRouterLinkClick }; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/index.ts index ded9310fe361a..237e0d342ed1f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/index.ts @@ -8,7 +8,6 @@ export { letBrowserHandleEvent } from './link_events'; export type { CreateHrefOptions } from './create_href'; export { createHref } from './create_href'; -export type { ReactRouterProps } from './generate_react_router_props'; export { generateReactRouterProps } from './generate_react_router_props'; export { EuiLinkTo, diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/types.ts b/x-pack/plugins/enterprise_search/public/applications/shared/types.ts index 51a83cb15cca5..095f1dddfcc4a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/types.ts @@ -5,7 +5,12 @@ * 2.0. */ +import type { ReactNode } from 'react'; + +import type { AppDeepLinkId, EuiSideNavItemTypeEnhanced } from '@kbn/core-chrome-browser'; + import { APP_SEARCH_PLUGIN, WORKPLACE_SEARCH_PLUGIN } from '../../../common/constants'; +import type { ProductAccess } from '../../../common/types'; import { ADD, UPDATE } from './constants/operations'; @@ -57,3 +62,37 @@ export interface SingleUserRoleMapping { roleMapping: T; hasEnterpriseSearchRole?: boolean; } + +export interface ReactRouterProps { + to: string; + onClick?(): void; + // Used to navigate outside of the React Router plugin basename but still within Kibana, + // e.g. if we need to go from Enterprise Search to App Search + shouldNotCreateHref?: boolean; + // Used if to is already a fully qualified URL that doesn't need basePath prepended + shouldNotPrepend?: boolean; +} + +export type GenerateNavLinkParameters = { + items?: Array>; // Primarily passed if using `items` to determine isSelected - if not, you can just set `items` outside of this helper + shouldShowActiveForSubroutes?: boolean; + to: string; +} & ReactRouterProps; + +export interface GenerateNavLinkFromDeepLinkParameters { + link: AppDeepLinkId; + shouldShowActiveForSubroutes?: boolean; +} + +export interface BuildClassicNavParameters { + productAccess: ProductAccess; +} + +export interface ClassicNavItem { + 'data-test-subj'?: string; + deepLink?: GenerateNavLinkFromDeepLinkParameters; + iconToString?: string; + id: string; + items?: ClassicNavItem[]; + name?: ReactNode; +} diff --git a/x-pack/plugins/enterprise_search/public/applications/test_helpers/test_utils.test_helper.tsx b/x-pack/plugins/enterprise_search/public/applications/test_helpers/test_utils.test_helper.tsx index d1729a50909ed..da30e6e93fadb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/test_helpers/test_utils.test_helper.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/test_helpers/test_utils.test_helper.tsx @@ -58,6 +58,7 @@ export const mockKibanaProps: KibanaLogicProps = { elasticsearch_host: 'https://your_deployment_url', }, getChromeStyle$: jest.fn().mockReturnValue(of('classic')), + getNavLinks: jest.fn().mockReturnValue([]), guidedOnboarding: {}, history: mockHistory, indexMappingComponent: () => { diff --git a/x-pack/plugins/enterprise_search/tsconfig.json b/x-pack/plugins/enterprise_search/tsconfig.json index fa0751078c0f7..7b7556729a76c 100644 --- a/x-pack/plugins/enterprise_search/tsconfig.json +++ b/x-pack/plugins/enterprise_search/tsconfig.json @@ -82,6 +82,7 @@ "@kbn/navigation-plugin", "@kbn/security-plugin-types-common", "@kbn/core-security-server", - "@kbn/core-security-server-mocks" + "@kbn/core-security-server-mocks", + "@kbn/unsaved-changes-prompt" ] } diff --git a/x-pack/plugins/entity_manager/public/lib/entity_client.test.ts b/x-pack/plugins/entity_manager/public/lib/entity_client.test.ts new file mode 100644 index 0000000000000..dbaf1205cdf98 --- /dev/null +++ b/x-pack/plugins/entity_manager/public/lib/entity_client.test.ts @@ -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 { EntityClient, EnitityInstance } from './entity_client'; +import { coreMock } from '@kbn/core/public/mocks'; + +const commonEntityFields: EnitityInstance = { + entity: { + last_seen_timestamp: '2023-10-09T00:00:00Z', + id: '1', + display_name: 'entity_name', + definition_id: 'entity_definition_id', + } as EnitityInstance['entity'], +}; + +describe('EntityClient', () => { + let entityClient: EntityClient; + + beforeEach(() => { + entityClient = new EntityClient(coreMock.createStart()); + }); + + describe('asKqlFilter', () => { + it('should return the kql filter', () => { + const entityLatest: EnitityInstance = { + entity: { + ...commonEntityFields.entity, + identity_fields: ['service.name', 'service.environment'], + type: 'service', + }, + service: { + name: 'my-service', + }, + }; + + const result = entityClient.asKqlFilter(entityLatest); + expect(result).toEqual('service.name: my-service'); + }); + + it('should return the kql filter when indentity_fields is composed by multiple fields', () => { + const entityLatest: EnitityInstance = { + entity: { + ...commonEntityFields.entity, + identity_fields: ['service.name', 'service.environment'], + type: 'service', + }, + service: { + name: 'my-service', + environment: 'staging', + }, + }; + + const result = entityClient.asKqlFilter(entityLatest); + expect(result).toEqual('(service.name: my-service AND service.environment: staging)'); + }); + + it('should ignore fields that are not present in the entity', () => { + const entityLatest: EnitityInstance = { + entity: { + ...commonEntityFields.entity, + identity_fields: ['host.name', 'foo.bar'], + }, + host: { + name: 'my-host', + }, + }; + + const result = entityClient.asKqlFilter(entityLatest); + expect(result).toEqual('host.name: my-host'); + }); + }); + + describe('getIdentityFieldsValue', () => { + it('should return identity fields values', () => { + const entityLatest: EnitityInstance = { + entity: { + ...commonEntityFields.entity, + identity_fields: ['service.name', 'service.environment'], + type: 'service', + }, + service: { + name: 'my-service', + }, + }; + + expect(entityClient.getIdentityFieldsValue(entityLatest)).toEqual({ + 'service.name': 'my-service', + }); + }); + + it('should return identity fields values when indentity_fields is composed by multiple fields', () => { + const entityLatest: EnitityInstance = { + entity: { + ...commonEntityFields.entity, + identity_fields: ['service.name', 'service.environment'], + type: 'service', + }, + service: { + name: 'my-service', + environment: 'staging', + }, + }; + + expect(entityClient.getIdentityFieldsValue(entityLatest)).toEqual({ + 'service.name': 'my-service', + 'service.environment': 'staging', + }); + }); + + it('should return identity fields when field is in the root', () => { + const entityLatest: EnitityInstance = { + entity: { + ...commonEntityFields.entity, + identity_fields: ['name'], + type: 'service', + }, + name: 'foo', + }; + + expect(entityClient.getIdentityFieldsValue(entityLatest)).toEqual({ + name: 'foo', + }); + }); + + it('should throw an error when identity fields are missing', () => { + const entityLatest: EnitityInstance = { + ...commonEntityFields, + }; + + expect(() => entityClient.getIdentityFieldsValue(entityLatest)).toThrow( + 'Identity fields are missing' + ); + }); + }); +}); diff --git a/x-pack/plugins/entity_manager/public/lib/entity_client.ts b/x-pack/plugins/entity_manager/public/lib/entity_client.ts index dc22a0b991b0d..08794873ba930 100644 --- a/x-pack/plugins/entity_manager/public/lib/entity_client.ts +++ b/x-pack/plugins/entity_manager/public/lib/entity_client.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { z } from '@kbn/zod'; import { CoreSetup, CoreStart } from '@kbn/core/public'; import { ClientRequestParamsOf, @@ -12,6 +13,9 @@ import { createRepositoryClient, isHttpFetchError, } from '@kbn/server-route-repository-client'; +import { type KueryNode, nodeTypes, toKqlExpression } from '@kbn/es-query'; +import { entityLatestSchema } from '@kbn/entities-schema'; +import { castArray } from 'lodash'; import { DisableManagedEntityResponse, EnableManagedEntityResponse, @@ -35,6 +39,8 @@ type CreateEntityDefinitionQuery = QueryParamOf< ClientRequestParamsOf >; +export type EnitityInstance = z.infer; + export class EntityClient { public readonly repositoryClient: EntityManagerRepositoryClient['fetch']; @@ -83,4 +89,38 @@ export class EntityClient { throw err; } } + + asKqlFilter(entityLatest: EnitityInstance) { + const identityFieldsValue = this.getIdentityFieldsValue(entityLatest); + + const nodes: KueryNode[] = Object.entries(identityFieldsValue).map(([identityField, value]) => { + return nodeTypes.function.buildNode('is', identityField, value); + }); + + if (nodes.length === 0) return ''; + + const kqlExpression = nodes.length > 1 ? nodeTypes.function.buildNode('and', nodes) : nodes[0]; + + return toKqlExpression(kqlExpression); + } + + getIdentityFieldsValue(entityLatest: EnitityInstance) { + const { identity_fields: identityFields } = entityLatest.entity; + + if (!identityFields) { + throw new Error('Identity fields are missing'); + } + + return castArray(identityFields).reduce((acc, field) => { + const value = field.split('.').reduce((obj: any, part: string) => { + return obj && typeof obj === 'object' ? (obj as Record)[part] : undefined; + }, entityLatest); + + if (value) { + acc[field] = value; + } + + return acc; + }, {} as Record); + } } diff --git a/x-pack/plugins/entity_manager/server/lib/entity_client.ts b/x-pack/plugins/entity_manager/server/lib/entity_client.ts index 67e9f52e32bf5..4e1dd263f9ca3 100644 --- a/x-pack/plugins/entity_manager/server/lib/entity_client.ts +++ b/x-pack/plugins/entity_manager/server/lib/entity_client.ts @@ -117,9 +117,7 @@ export class EntityClient { }); if (!definition) { - const message = `Unable to find entity definition [${id}]`; - this.options.logger.error(message); - throw new EntityDefinitionNotFound(message); + throw new EntityDefinitionNotFound(`Unable to find entity definition [${id}]`); } this.options.logger.info( diff --git a/x-pack/plugins/file_upload/public/components/__snapshots__/import_complete_view.test.tsx.snap b/x-pack/plugins/file_upload/public/components/__snapshots__/import_complete_view.test.tsx.snap index 8dd4858dfea5d..4108062e4fec4 100644 --- a/x-pack/plugins/file_upload/public/components/__snapshots__/import_complete_view.test.tsx.snap +++ b/x-pack/plugins/file_upload/public/components/__snapshots__/import_complete_view.test.tsx.snap @@ -348,7 +348,7 @@ exports[`Should render success 1`] = ` /> { , logge .addVersion( { version: '1', + security: { + authz: { + enabled: false, + reason: + 'This route is opted out from authorization because permissions will be checked by elasticsearch', + }, + }, validate: { request: { query: schema.object({ @@ -155,6 +162,13 @@ export function fileUploadRoutes(coreSetup: CoreSetup, logge .addVersion( { version: '1', + security: { + authz: { + enabled: false, + reason: + 'This route is opted out from authorization because permissions will be checked by elasticsearch', + }, + }, validate: { request: { query: importFileQuerySchema, @@ -206,6 +220,13 @@ export function fileUploadRoutes(coreSetup: CoreSetup, logge .addVersion( { version: '1', + security: { + authz: { + enabled: false, + reason: + 'This route is opted out from authorization because permissions will be checked by elasticsearch', + }, + }, validate: { request: { body: schema.object({ index: schema.string() }), diff --git a/x-pack/plugins/fleet/common/constants/routes.ts b/x-pack/plugins/fleet/common/constants/routes.ts index c071c6feecbf8..61d50d0f9e073 100644 --- a/x-pack/plugins/fleet/common/constants/routes.ts +++ b/x-pack/plugins/fleet/common/constants/routes.ts @@ -135,8 +135,6 @@ export const APP_API_ROUTES = { CHECK_PERMISSIONS_PATTERN: `${API_ROOT}/check-permissions`, GENERATE_SERVICE_TOKEN_PATTERN: `${API_ROOT}/service_tokens`, AGENT_POLICIES_SPACES: `${INTERNAL_ROOT}/agent_policies_spaces`, - // deprecated since 8.0 - GENERATE_SERVICE_TOKEN_PATTERN_DEPRECATED: `${API_ROOT}/service-tokens`, }; // Agent API routes @@ -159,8 +157,6 @@ export const AGENT_API_ROUTES = { AVAILABLE_VERSIONS_PATTERN: `${API_ROOT}/agents/available_versions`, STATUS_PATTERN: `${API_ROOT}/agent_status`, DATA_PATTERN: `${API_ROOT}/agent_status/data`, - // deprecated since 8.0 - STATUS_PATTERN_DEPRECATED: `${API_ROOT}/agent-status`, UPGRADE_PATTERN: `${API_ROOT}/agents/{agentId}/upgrade`, BULK_UPGRADE_PATTERN: `${API_ROOT}/agents/bulk_upgrade`, ACTION_STATUS_PATTERN: `${API_ROOT}/agents/action_status`, diff --git a/x-pack/plugins/fleet/common/services/agent_policies_helpers.ts b/x-pack/plugins/fleet/common/services/agent_policies_helpers.ts index 8a1e268614684..5729947feea31 100644 --- a/x-pack/plugins/fleet/common/services/agent_policies_helpers.ts +++ b/x-pack/plugins/fleet/common/services/agent_policies_helpers.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; + import type { NewAgentPolicy, AgentPolicy } from '../types'; import { FLEET_SERVER_PACKAGE, @@ -13,6 +15,12 @@ import { FLEET_ENDPOINT_PACKAGE, } from '../constants'; +export function getDefaultFleetServerpolicyId(spaceId?: string) { + return !spaceId || spaceId === '' || spaceId === DEFAULT_SPACE_ID + ? 'fleet-server-policy' + : `${spaceId}-fleet-server-policy`; +} + export function policyHasFleetServer( agentPolicy: Pick ) { diff --git a/x-pack/plugins/fleet/common/types/models/epm.ts b/x-pack/plugins/fleet/common/types/models/epm.ts index 3aa65dc3adcd4..827130d802f22 100644 --- a/x-pack/plugins/fleet/common/types/models/epm.ts +++ b/x-pack/plugins/fleet/common/types/models/epm.ts @@ -124,10 +124,25 @@ export type InstallablePackage = RegistryPackage | ArchivePackage; export type AssetsMap = Map; +export interface ArchiveEntry { + path: string; + buffer?: Buffer; +} + +export interface ArchiveIterator { + traverseEntries: (onEntry: (entry: ArchiveEntry) => Promise) => Promise; + getPaths: () => Promise; +} + export interface PackageInstallContext { packageInfo: InstallablePackage; + /** + * @deprecated Use `archiveIterator` to access the package archive entries + * without loading them all into memory at once. + */ assetsMap: AssetsMap; paths: string[]; + archiveIterator: ArchiveIterator; } export type ArchivePackage = PackageSpecManifest & diff --git a/x-pack/plugins/fleet/common/types/rest_spec/agent.ts b/x-pack/plugins/fleet/common/types/rest_spec/agent.ts index bb69346bbdce4..b990e5367bb42 100644 --- a/x-pack/plugins/fleet/common/types/rest_spec/agent.ts +++ b/x-pack/plugins/fleet/common/types/rest_spec/agent.ts @@ -21,15 +21,13 @@ import type { ListResult, ListWithKuery } from './common'; export interface GetAgentsRequest { query: ListWithKuery & { - showInactive: boolean; + showInactive?: boolean; showUpgradeable?: boolean; withMetrics?: boolean; }; } export interface GetAgentsResponse extends ListResult { - // deprecated in 8.x - list?: Agent[]; statusSummary?: Record; } @@ -128,16 +126,6 @@ export type PostBulkAgentUpgradeResponse = BulkAgentAction; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface PostAgentUpgradeResponse {} -// deprecated -export interface PutAgentReassignRequest { - params: { - agentId: string; - }; - body: { policy_id: string }; -} -// deprecated -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface PutAgentReassignResponse {} export interface PostAgentReassignRequest { params: { agentId: string; @@ -217,8 +205,6 @@ export interface GetAgentStatusRequest { export interface GetAgentStatusResponse { results: { events: number; - // deprecated - total: number; online: number; error: number; offline: number; diff --git a/x-pack/plugins/fleet/cypress/tasks/common.ts b/x-pack/plugins/fleet/cypress/tasks/common.ts index cf161640bf03f..2bf201b11a498 100644 --- a/x-pack/plugins/fleet/cypress/tasks/common.ts +++ b/x-pack/plugins/fleet/cypress/tasks/common.ts @@ -67,7 +67,6 @@ export const internalRequest = ({ const NEW_FEATURES_TOUR_STORAGE_KEYS = { RULE_MANAGEMENT_PAGE: 'securitySolution.rulesManagementPage.newFeaturesTour.v8.9', TIMELINES: 'securitySolution.security.timelineFlyoutHeader.saveTimelineTour', - FLYOUT: 'securitySolution.documentDetails.newFeaturesTour.v8.14', }; const disableNewFeaturesTours = (window: Window) => { diff --git a/x-pack/plugins/fleet/kibana.jsonc b/x-pack/plugins/fleet/kibana.jsonc index dec968457f294..823328da8ada6 100644 --- a/x-pack/plugins/fleet/kibana.jsonc +++ b/x-pack/plugins/fleet/kibana.jsonc @@ -29,7 +29,8 @@ "uiActions", "dashboard", "fieldsMetadata", - "logsDataAccess" + "logsDataAccess", + "spaces" ], "optionalPlugins": [ "features", @@ -40,7 +41,6 @@ "telemetry", "discover", "ingestPipelines", - "spaces", "guidedOnboarding", "integrationAssistant" ], diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_quick_start_form.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_quick_start_form.ts index e56ae45b1661a..559fcad522351 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_quick_start_form.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_quick_start_form.ts @@ -5,30 +5,35 @@ * 2.0. */ -import { useState, useCallback, useEffect } from 'react'; +import { useState, useCallback, useEffect, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; +import { getDefaultFleetServerpolicyId } from '../../../../../../common/services/agent_policies_helpers'; import type { useComboInput, useInput, useSwitchInput } from '../../../hooks'; -import { sendCreateAgentPolicy, sendGetOneAgentPolicy, useStartServices } from '../../../hooks'; - +import { + sendCreateAgentPolicy, + sendGetOneAgentPolicy, + useFleetStatus, + useStartServices, +} from '../../../hooks'; import type { NewAgentPolicy } from '../../../types'; - import type { FleetServerHost } from '../../../types'; - import { useServiceToken } from '../../../hooks/use_service_token'; import { useSelectFleetServerPolicy } from './use_select_fleet_server_policy'; import { useFleetServerHost } from './use_fleet_server_host'; -const QUICK_START_FLEET_SERVER_POLICY_FIELDS: NewAgentPolicy = { - id: 'fleet-server-policy', - name: 'Fleet Server Policy', - description: 'Fleet Server policy generated by Kibana', - namespace: 'default', - has_fleet_server: true, - monitoring_enabled: ['logs', 'metrics'], - is_default_fleet_server: true, -}; +function getQuickStartFleetServerPolicyFields(spaceId?: string): NewAgentPolicy { + return { + id: getDefaultFleetServerpolicyId(spaceId), + name: 'Fleet Server Policy', + description: 'Fleet Server policy generated by Kibana', + namespace: 'default', + has_fleet_server: true, + monitoring_enabled: ['logs', 'metrics'], + is_default_fleet_server: true, + }; +} export type QuickStartCreateFormStatus = 'initial' | 'loading' | 'error' | 'success'; @@ -69,6 +74,7 @@ export const useQuickStartCreateForm = (): QuickStartCreateForm => { setFleetServerHost, inputs, } = useFleetServerHost(); + const { spaceId } = useFleetStatus(); // When a validation error is surfaced from the Fleet Server host form, we want to treat it // the same way we do errors from the service token or policy creation steps @@ -81,6 +87,11 @@ export const useQuickStartCreateForm = (): QuickStartCreateForm => { const { fleetServerPolicyId, setFleetServerPolicyId } = useSelectFleetServerPolicy(); const { serviceToken, generateServiceToken } = useServiceToken(); + const quickStartFleetServerPolicyFields = useMemo( + () => getQuickStartFleetServerPolicyFields(spaceId), + [spaceId] + ); + const submit = useCallback(async () => { try { if (!fleetServerHost || fleetServerHost) { @@ -98,16 +109,14 @@ export const useQuickStartCreateForm = (): QuickStartCreateForm => { await generateServiceToken(); - const existingPolicy = await sendGetOneAgentPolicy( - QUICK_START_FLEET_SERVER_POLICY_FIELDS.id! - ); + const existingPolicy = await sendGetOneAgentPolicy(quickStartFleetServerPolicyFields.id!); // Don't attempt to create the policy if it's already been created in a previous quick start flow if (existingPolicy.data?.item) { setFleetServerPolicyId(existingPolicy.data?.item.id); } else { const createPolicyResponse = await sendCreateAgentPolicy( - QUICK_START_FLEET_SERVER_POLICY_FIELDS, + quickStartFleetServerPolicyFields, { withSysMonitoring: true, } @@ -134,6 +143,7 @@ export const useQuickStartCreateForm = (): QuickStartCreateForm => { generateServiceToken, setFleetServerPolicyId, notifications.toasts, + quickStartFleetServerPolicyFields, ]); return { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/actions_menu.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/actions_menu.tsx index f4f519b0a9c95..88dd00546e51f 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/actions_menu.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/actions_menu.tsx @@ -137,13 +137,22 @@ export const AgentPolicyActionMenu = memo<{ const copyPolicyItem = ( { setIsContextMenuOpen(false); copyAgentPolicyPrompt(agentPolicy, onCopySuccess); }} key="copyPolicy" + toolTipContent={ + hasManagedPackagePolicy ? ( + + ) : undefined + } > ( if (isFleetEnabled) { setIsLoading(true); const { data } = await sendGetAgentStatus({ policyId: agentPolicy.id }); - if (data?.results.total) { - setAgentCount(data.results.total); + if (data?.results.active) { + setAgentCount(data.results.active); } else { await submitUpdateAgentPolicy(); } diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx index 6157f09968680..9522d02756887 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx @@ -166,8 +166,8 @@ export const EditPackagePolicyForm = memo<{ let count = 0; for (const id of packagePolicy.policy_ids) { const { data } = await sendGetAgentStatus({ policyId: id }); - if (data?.results.total) { - count += data.results.total; + if (data?.results.active) { + count += data.results.active; } } setAgentCount(count); diff --git a/x-pack/plugins/fleet/server/config.ts b/x-pack/plugins/fleet/server/config.ts index 746498221de55..ab5e06ef03716 100644 --- a/x-pack/plugins/fleet/server/config.ts +++ b/x-pack/plugins/fleet/server/config.ts @@ -201,64 +201,68 @@ export const config: PluginConfigDescriptor = { defaultValue: () => [], }), - internal: schema.maybe( - schema.object({ - disableILMPolicies: schema.boolean({ - defaultValue: false, - }), - fleetServerStandalone: schema.boolean({ - defaultValue: false, - }), - onlyAllowAgentUpgradeToKnownVersions: schema.boolean({ - defaultValue: false, - }), - activeAgentsSoftLimit: schema.maybe( - schema.number({ - min: 0, - }) - ), - retrySetupOnBoot: schema.boolean({ defaultValue: false }), - registry: schema.object( - { - kibanaVersionCheckEnabled: schema.boolean({ defaultValue: true }), - excludePackages: schema.arrayOf(schema.string(), { defaultValue: [] }), - spec: schema.object( - { - min: schema.maybe(schema.string()), - max: schema.string({ defaultValue: REGISTRY_SPEC_MAX_VERSION }), - }, - { - defaultValue: { - max: REGISTRY_SPEC_MAX_VERSION, - }, - } - ), - capabilities: schema.arrayOf( - schema.oneOf([ - // See package-spec for the list of available capiblities https://github.com/elastic/package-spec/blob/dcc37b652690f8a2bca9cf8a12fc28fd015730a0/spec/integration/manifest.spec.yml#L113 - schema.literal('apm'), - schema.literal('enterprise_search'), - schema.literal('observability'), - schema.literal('security'), - schema.literal('serverless_search'), - schema.literal('uptime'), - ]), - { defaultValue: [] } - ), - }, - { - defaultValue: { - kibanaVersionCheckEnabled: true, - capabilities: [], - excludePackages: [], - spec: { + internal: schema.object({ + disableILMPolicies: schema.boolean({ + defaultValue: false, + }), + fleetServerStandalone: schema.boolean({ + defaultValue: false, + }), + onlyAllowAgentUpgradeToKnownVersions: schema.boolean({ + defaultValue: false, + }), + activeAgentsSoftLimit: schema.maybe( + schema.number({ + min: 0, + }) + ), + retrySetupOnBoot: schema.boolean({ defaultValue: false }), + registry: schema.object( + { + // Must be set back to `true` before v9 release + // Requires all registry packages to add v9 as a compatible semver range + // https://github.com/elastic/kibana/issues/192624 + kibanaVersionCheckEnabled: schema.boolean({ defaultValue: false }), + excludePackages: schema.arrayOf(schema.string(), { defaultValue: [] }), + spec: schema.object( + { + min: schema.maybe(schema.string()), + max: schema.string({ defaultValue: REGISTRY_SPEC_MAX_VERSION }), + }, + { + defaultValue: { max: REGISTRY_SPEC_MAX_VERSION, }, + } + ), + capabilities: schema.arrayOf( + schema.oneOf([ + // See package-spec for the list of available capiblities https://github.com/elastic/package-spec/blob/dcc37b652690f8a2bca9cf8a12fc28fd015730a0/spec/integration/manifest.spec.yml#L113 + schema.literal('apm'), + schema.literal('enterprise_search'), + schema.literal('observability'), + schema.literal('security'), + schema.literal('serverless_search'), + schema.literal('uptime'), + ]), + { defaultValue: [] } + ), + }, + { + defaultValue: { + // Must be set back to `true` before v9 release + // Requires all registry packages to add v9 as a compatible semver range + // https://github.com/elastic/kibana/issues/192624 + kibanaVersionCheckEnabled: false, + capabilities: [], + excludePackages: [], + spec: { + max: REGISTRY_SPEC_MAX_VERSION, }, - } - ), - }) - ), + }, + } + ), + }), enabled: schema.boolean({ defaultValue: true }), /** * The max size of the artifacts encoded_size sum in a batch when more than one (there is at least one artifact in a batch). diff --git a/x-pack/plugins/fleet/server/routes/agent/handlers.ts b/x-pack/plugins/fleet/server/routes/agent/handlers.ts index 4f4b5592f2d04..76d0fefe90fde 100644 --- a/x-pack/plugins/fleet/server/routes/agent/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent/handlers.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { uniq } from 'lodash'; +import { omit, uniq } from 'lodash'; import { type RequestHandler, SavedObjectsErrorHelpers } from '@kbn/core/server'; import type { TypeOf } from '@kbn/config-schema'; @@ -13,7 +13,6 @@ import type { GetAgentsResponse, GetOneAgentResponse, GetAgentStatusResponse, - PutAgentReassignResponse, GetAgentTagsResponse, GetAvailableVersionsResponse, GetActionStatusResponse, @@ -30,7 +29,6 @@ import type { DeleteAgentRequestSchema, GetAgentStatusRequestSchema, GetAgentDataRequestSchema, - PutAgentReassignRequestSchemaDeprecated, PostAgentReassignRequestSchema, PostBulkAgentReassignRequestSchema, PostBulkUpdateAgentTagsRequestSchema, @@ -207,7 +205,6 @@ export const getAgentsHandler: FleetRequestHandler< } const body: GetAgentsResponse = { - list: agents, // deprecated items: agents, total, page, @@ -243,29 +240,6 @@ export const getAgentTagsHandler: RequestHandler< } }; -export const putAgentsReassignHandlerDeprecated: RequestHandler< - TypeOf, - undefined, - TypeOf -> = async (context, request, response) => { - const coreContext = await context.core; - const soClient = coreContext.savedObjects.client; - const esClient = coreContext.elasticsearch.client.asInternalUser; - try { - await AgentService.reassignAgent( - soClient, - esClient, - request.params.agentId, - request.body.policy_id - ); - - const body: PutAgentReassignResponse = {}; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } -}; - export const postAgentReassignHandler: RequestHandler< TypeOf, undefined, @@ -341,7 +315,7 @@ export const getAgentStatusForAgentPolicyHandler: FleetRequestHandler< parsePolicyIds(request.query.policyIds) ); - const body: GetAgentStatusResponse = { results }; + const body: GetAgentStatusResponse = { results: omit(results, 'total') }; return response.ok({ body }); } catch (error) { diff --git a/x-pack/plugins/fleet/server/routes/agent/index.test.ts b/x-pack/plugins/fleet/server/routes/agent/index.test.ts index 7f210028ff742..34f66da425da3 100644 --- a/x-pack/plugins/fleet/server/routes/agent/index.test.ts +++ b/x-pack/plugins/fleet/server/routes/agent/index.test.ts @@ -222,7 +222,6 @@ describe('schema validation', () => { it('list agents should return valid response', async () => { const expectedResponse: GetAgentsResponse = { items: [agent], - list: [agent], total: 1, page: 1, perPage: 1, @@ -366,7 +365,6 @@ describe('schema validation', () => { const expectedResponse: GetAgentStatusResponse = { results: { events: 1, - total: 1, online: 1, error: 1, offline: 1, diff --git a/x-pack/plugins/fleet/server/routes/agent/index.ts b/x-pack/plugins/fleet/server/routes/agent/index.ts index fc45869dc1219..1c40f36a7e481 100644 --- a/x-pack/plugins/fleet/server/routes/agent/index.ts +++ b/x-pack/plugins/fleet/server/routes/agent/index.ts @@ -23,7 +23,6 @@ import { GetAgentStatusRequestSchema, GetAgentDataRequestSchema, PostNewAgentActionRequestSchema, - PutAgentReassignRequestSchemaDeprecated, PostAgentReassignRequestSchema, PostBulkAgentReassignRequestSchema, PostAgentUpgradeRequestSchema, @@ -68,7 +67,6 @@ import { updateAgentHandler, deleteAgentHandler, getAgentStatusForAgentPolicyHandler, - putAgentsReassignHandlerDeprecated, postBulkAgentReassignHandler, getAgentDataHandler, bulkUpdateAgentTagsHandler, @@ -391,23 +389,6 @@ export const registerAPIRoutes = (router: FleetAuthzRouter, config: FleetConfigT postAgentUnenrollHandler ); - router.versioned - .put({ - path: AGENT_API_ROUTES.REASSIGN_PATTERN, - fleetAuthz: { - fleet: { allAgents: true }, - }, - // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} - deprecated: true, - }) - .addVersion( - { - version: API_VERSIONS.public.v1, - validate: { request: PutAgentReassignRequestSchemaDeprecated }, - }, - putAgentsReassignHandlerDeprecated - ); - router.versioned .post({ path: AGENT_API_ROUTES.REASSIGN_PATTERN, @@ -613,22 +594,6 @@ export const registerAPIRoutes = (router: FleetAuthzRouter, config: FleetConfigT }, getAgentStatusForAgentPolicyHandler ); - router.versioned - .get({ - path: AGENT_API_ROUTES.STATUS_PATTERN_DEPRECATED, - fleetAuthz: { - fleet: { readAgents: true }, - }, - // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} - deprecated: true, - }) - .addVersion( - { - version: API_VERSIONS.public.v1, - validate: { request: GetAgentStatusRequestSchema }, - }, - getAgentStatusForAgentPolicyHandler - ); // Agent data router.versioned .get({ diff --git a/x-pack/plugins/fleet/server/routes/app/index.ts b/x-pack/plugins/fleet/server/routes/app/index.ts index c0b7dbcfa1743..e66f9f02a687b 100644 --- a/x-pack/plugins/fleet/server/routes/app/index.ts +++ b/x-pack/plugins/fleet/server/routes/app/index.ts @@ -285,22 +285,4 @@ export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType }, generateServiceTokenHandler ); - - router.versioned - .post({ - path: APP_API_ROUTES.GENERATE_SERVICE_TOKEN_PATTERN_DEPRECATED, - fleetAuthz: { - fleet: { allAgents: true }, - }, - description: `Create a service token`, - // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} - deprecated: true, - }) - .addVersion( - { - version: API_VERSIONS.public.v1, - validate: {}, - }, - generateServiceTokenHandler - ); }; 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 index 1eb8387f69751..5690c32c2d7fd 100644 --- a/x-pack/plugins/fleet/server/routes/epm/file_handler.test.ts +++ b/x-pack/plugins/fleet/server/routes/epm/file_handler.test.ts @@ -15,7 +15,7 @@ import { getBundledPackageByPkgKey } from '../../services/epm/packages/bundled_p import { getFile, getInstallation } from '../../services/epm/packages/get'; import type { FleetRequestHandlerContext } from '../..'; import { appContextService } from '../../services'; -import { unpackBufferEntries } from '../../services/epm/archive'; +import { unpackArchiveEntriesIntoMemory } from '../../services/epm/archive'; import { getAsset } from '../../services/epm/archive/storage'; import { getFileHandler } from './file_handler'; @@ -29,7 +29,7 @@ jest.mock('../../services/epm/packages/get'); const mockedGetBundledPackageByPkgKey = jest.mocked(getBundledPackageByPkgKey); const mockedGetInstallation = jest.mocked(getInstallation); const mockedGetFile = jest.mocked(getFile); -const mockedUnpackBufferEntries = jest.mocked(unpackBufferEntries); +const mockedUnpackBufferEntries = jest.mocked(unpackArchiveEntriesIntoMemory); const mockedGetAsset = jest.mocked(getAsset); function mockContext() { diff --git a/x-pack/plugins/fleet/server/routes/epm/file_handler.ts b/x-pack/plugins/fleet/server/routes/epm/file_handler.ts index 0f22a31c1aa72..994f52a71c224 100644 --- a/x-pack/plugins/fleet/server/routes/epm/file_handler.ts +++ b/x-pack/plugins/fleet/server/routes/epm/file_handler.ts @@ -17,7 +17,7 @@ import { defaultFleetErrorHandler } from '../../errors'; 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'; +import { unpackArchiveEntriesIntoMemory } from '../../services/epm/archive'; const CACHE_CONTROL_10_MINUTES_HEADER: HttpResponseOptions['headers'] = { 'cache-control': 'max-age=600', @@ -69,7 +69,7 @@ export const getFileHandler: FleetRequestHandler< pkgToPkgKey({ name: pkgName, version: pkgVersion }) ); if (bundledPackage) { - const bufferEntries = await unpackBufferEntries( + const bufferEntries = await unpackArchiveEntriesIntoMemory( await bundledPackage.getBuffer(), 'application/zip' ); diff --git a/x-pack/plugins/fleet/server/routes/epm/kibana_assets_handler.ts b/x-pack/plugins/fleet/server/routes/epm/kibana_assets_handler.ts index 8fe83f98669d1..ad0bec6397ee8 100644 --- a/x-pack/plugins/fleet/server/routes/epm/kibana_assets_handler.ts +++ b/x-pack/plugins/fleet/server/routes/epm/kibana_assets_handler.ts @@ -22,6 +22,7 @@ import type { FleetRequestHandler, InstallKibanaAssetsRequestSchema, } from '../../types'; +import { createArchiveIteratorFromMap } from '../../services/epm/archive/archive_iterator'; export const installPackageKibanaAssetsHandler: FleetRequestHandler< TypeOf, @@ -69,6 +70,7 @@ export const installPackageKibanaAssetsHandler: FleetRequestHandler< packageInfo, paths: installedPkgWithAssets.paths, assetsMap: installedPkgWithAssets.assetsMap, + archiveIterator: createArchiveIteratorFromMap(installedPkgWithAssets.assetsMap), }, }); 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 608b6f739fc29..fb3274b6eef77 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.test.ts @@ -1319,6 +1319,36 @@ describe('Agent policy', () => { }); }); + describe('copy', () => { + let soClient: ReturnType; + let esClient: ReturnType['asInternalUser']; + + beforeEach(() => { + soClient = getSavedObjectMock({ revision: 1, package_policies: ['package-1'] }); + esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + }); + + it('should throw error for agent policy which has managed package policy', async () => { + mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([ + { + id: 'package-1', + is_managed: true, + }, + ] as any); + try { + await agentPolicyService.copy(soClient, esClient, 'mocked', { + name: 'copy mocked', + }); + } catch (e) { + expect(e.message).toEqual( + new PackagePolicyRestrictionRelatedError( + `Cannot copy an agent policy mocked that contains managed package policies` + ).message + ); + } + }); + }); + describe('deployPolicy', () => { beforeEach(() => { mockedGetFullAgentPolicy.mockReset(); diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index bcbeafdde1182..cada1c8e64452 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -30,6 +30,8 @@ import { asyncForEach } from '@kbn/std'; import type { SavedObjectError } from '@kbn/core-saved-objects-common'; +import { withSpan } from '@kbn/apm-utils'; + import { getAllowedOutputTypeForPolicy, packageToPackagePolicy, @@ -170,11 +172,13 @@ class AgentPolicyService { removeProtection: boolean; skipValidation: boolean; returnUpdatedPolicy?: boolean; + asyncDeploy?: boolean; } = { bumpRevision: true, removeProtection: false, skipValidation: false, returnUpdatedPolicy: true, + asyncDeploy: false, } ): Promise { const savedObjectType = await getAgentPolicySavedObjectType(); @@ -228,10 +232,19 @@ class AgentPolicyService { newAgentPolicy!.package_policies = existingAgentPolicy.package_policies; if (options.bumpRevision || options.removeProtection) { - await this.triggerAgentPolicyUpdatedEvent(esClient, 'updated', id, { - spaceId: soClient.getCurrentNamespace(), - agentPolicy: newAgentPolicy, - }); + if (!options.asyncDeploy) { + await this.triggerAgentPolicyUpdatedEvent(esClient, 'updated', id, { + spaceId: soClient.getCurrentNamespace(), + agentPolicy: newAgentPolicy, + }); + } else { + await scheduleDeployAgentPoliciesTask(appContextService.getTaskManagerStart()!, [ + { + id, + spaceId: soClient.getCurrentNamespace(), + }, + ]); + } } logger.debug( `Agent policy ${id} update completed, revision: ${ @@ -771,6 +784,17 @@ class AgentPolicyService { if (!baseAgentPolicy) { throw new AgentPolicyNotFoundError('Agent policy not found'); } + if (baseAgentPolicy.package_policies?.length) { + const hasManagedPackagePolicies = baseAgentPolicy.package_policies.some( + (packagePolicy) => packagePolicy.is_managed + ); + if (hasManagedPackagePolicies) { + throw new PackagePolicyRestrictionRelatedError( + `Cannot copy an agent policy ${id} that contains managed package policies` + ); + } + } + const newAgentPolicy = await this.create( soClient, esClient, @@ -867,13 +891,16 @@ class AgentPolicyService { soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, id: string, - options?: { user?: AuthenticatedUser; removeProtection?: boolean } + options?: { user?: AuthenticatedUser; removeProtection?: boolean; asyncDeploy?: boolean } ): Promise { - await this._update(soClient, esClient, id, {}, options?.user, { - bumpRevision: true, - removeProtection: options?.removeProtection ?? false, - skipValidation: false, - returnUpdatedPolicy: false, + return withSpan('bump_agent_policy_revision', async () => { + await this._update(soClient, esClient, id, {}, options?.user, { + bumpRevision: true, + removeProtection: options?.removeProtection ?? false, + skipValidation: false, + returnUpdatedPolicy: false, + asyncDeploy: options?.asyncDeploy, + }); }); } 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 f370867fc493b..3902548581595 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy_create.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy_create.ts @@ -11,6 +11,7 @@ import type { SavedObjectsClientContract, } from '@kbn/core/server'; +import { getDefaultFleetServerpolicyId } from '../../common/services/agent_policies_helpers'; import type { HTTPAuthorizationHeader } from '../../common/http_authorization_header'; import { @@ -27,23 +28,25 @@ import { bulkInstallPackages } from './epm/packages'; import { ensureDefaultEnrollmentAPIKeyForAgentPolicy } from './api_keys'; import { agentlessAgentService } from './agents/agentless_agent'; -const FLEET_SERVER_POLICY_ID = 'fleet-server-policy'; - async function getFleetServerAgentPolicyId( soClient: SavedObjectsClientContract ): Promise { let agentPolicyId; - // creating first fleet server policy with id 'fleet-server-policy' + // creating first fleet server policy with id '(space-)?fleet-server-policy' let agentPolicy; try { - agentPolicy = await agentPolicyService.get(soClient, FLEET_SERVER_POLICY_ID, false); + agentPolicy = await agentPolicyService.get( + soClient, + getDefaultFleetServerpolicyId(soClient.getCurrentNamespace()), + false + ); } catch (err) { if (!err.isBoom || err.output.statusCode !== 404) { throw err; } } if (!agentPolicy) { - agentPolicyId = FLEET_SERVER_POLICY_ID; + agentPolicyId = getDefaultFleetServerpolicyId(soClient.getCurrentNamespace()); } return agentPolicyId; } @@ -118,7 +121,7 @@ export async function createAgentPolicyWithPackages({ packagesToInstall.push(FLEET_SERVER_PACKAGE); agentPolicyId = agentPolicyId || (await getFleetServerAgentPolicyId(soClient)); - if (agentPolicyId === FLEET_SERVER_POLICY_ID) { + if (agentPolicyId === getDefaultFleetServerpolicyId(spaceId)) { // setting first fleet server policy to default, so that fleet server can enroll without setting policy_id newPolicy.is_default_fleet_server = true; } diff --git a/x-pack/plugins/fleet/server/services/epm/archive/archive_iterator.ts b/x-pack/plugins/fleet/server/services/epm/archive/archive_iterator.ts new file mode 100644 index 0000000000000..369b32412bd82 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/epm/archive/archive_iterator.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AssetsMap, ArchiveIterator, ArchiveEntry } from '../../../../common/types'; + +import { traverseArchiveEntries } from '.'; + +/** + * Creates an iterator for traversing and extracting paths from an archive + * buffer. This iterator is intended to be used for memory efficient traversal + * of archive contents without extracting the entire archive into memory. + * + * @param archiveBuffer - The buffer containing the archive data. + * @param contentType - The content type of the archive (e.g., + * 'application/zip'). + * @returns ArchiveIterator instance. + * + */ +export const createArchiveIterator = ( + archiveBuffer: Buffer, + contentType: string +): ArchiveIterator => { + const paths: string[] = []; + + const traverseEntries = async ( + onEntry: (entry: ArchiveEntry) => Promise + ): Promise => { + await traverseArchiveEntries(archiveBuffer, contentType, async (entry) => { + await onEntry(entry); + }); + }; + + const getPaths = async (): Promise => { + if (paths.length) { + return paths; + } + + await traverseEntries(async (entry) => { + paths.push(entry.path); + }); + + return paths; + }; + + return { + traverseEntries, + getPaths, + }; +}; + +/** + * Creates an archive iterator from the assetsMap. This is a stop-gap solution + * to provide a uniform interface for traversing assets while assetsMap is still + * in use. It works with a map of assets loaded into memory and is not intended + * for use with large archives. + * + * @param assetsMap - A map where the keys are asset paths and the values are + * asset buffers. + * @returns ArchiveIterator instance. + * + */ +export const createArchiveIteratorFromMap = (assetsMap: AssetsMap): ArchiveIterator => { + const traverseEntries = async ( + onEntry: (entry: ArchiveEntry) => Promise + ): Promise => { + for (const [path, buffer] of assetsMap) { + await onEntry({ path, buffer }); + } + }; + + const getPaths = async (): Promise => { + return [...assetsMap.keys()]; + }; + + return { + traverseEntries, + getPaths, + }; +}; diff --git a/x-pack/plugins/fleet/server/services/epm/archive/extract.ts b/x-pack/plugins/fleet/server/services/epm/archive/extract.ts index 84aa161385cb3..9f5f90959d144 100644 --- a/x-pack/plugins/fleet/server/services/epm/archive/extract.ts +++ b/x-pack/plugins/fleet/server/services/epm/archive/extract.ts @@ -11,13 +11,12 @@ import * as tar from 'tar'; import yauzl from 'yauzl'; import { bufferToStream, streamToBuffer } from '../streams'; - -import type { ArchiveEntry } from '.'; +import type { ArchiveEntry } from '../../../../common/types'; export async function untarBuffer( buffer: Buffer, filter = (entry: ArchiveEntry): boolean => true, - onEntry = (entry: ArchiveEntry): void => {} + onEntry = async (entry: ArchiveEntry): Promise => {} ) { const deflatedStream = bufferToStream(buffer); // use tar.list vs .extract to avoid writing to disk @@ -37,7 +36,7 @@ export async function untarBuffer( export async function unzipBuffer( buffer: Buffer, filter = (entry: ArchiveEntry): boolean => true, - onEntry = (entry: ArchiveEntry): void => {} + onEntry = async (entry: ArchiveEntry): Promise => {} ): Promise { const zipfile = await yauzlFromBuffer(buffer, { lazyEntries: true }); zipfile.readEntry(); @@ -45,9 +44,12 @@ export async function unzipBuffer( const path = entry.fileName; if (!filter({ path })) return zipfile.readEntry(); - const entryBuffer = await getZipReadStream(zipfile, entry).then(streamToBuffer); - onEntry({ buffer: entryBuffer, path }); - zipfile.readEntry(); + try { + const entryBuffer = await getZipReadStream(zipfile, entry).then(streamToBuffer); + await onEntry({ buffer: entryBuffer, path }); + } finally { + zipfile.readEntry(); + } }); return new Promise((resolve, reject) => zipfile.on('end', resolve).on('error', reject)); } diff --git a/x-pack/plugins/fleet/server/services/epm/archive/index.ts b/x-pack/plugins/fleet/server/services/epm/archive/index.ts index 5943f8f838fcb..ed9ff2a5e4b72 100644 --- a/x-pack/plugins/fleet/server/services/epm/archive/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/archive/index.ts @@ -5,13 +5,20 @@ * 2.0. */ -import type { AssetParts, AssetsMap } from '../../../../common/types'; +import type { + ArchiveEntry, + ArchiveIterator, + AssetParts, + AssetsMap, +} from '../../../../common/types'; import { PackageInvalidArchiveError, PackageUnsupportedMediaTypeError, PackageNotFoundError, } from '../../../errors'; +import { createArchiveIterator } from './archive_iterator'; + import { deletePackageInfo } from './cache'; import type { SharedKey } from './cache'; import { getBufferExtractor } from './extract'; @@ -20,66 +27,85 @@ export * from './cache'; export { getBufferExtractor, untarBuffer, unzipBuffer } from './extract'; export { generatePackageInfoFromArchiveBuffer } from './parse'; -export interface ArchiveEntry { - path: string; - buffer?: Buffer; -} - export async function unpackBufferToAssetsMap({ - name, - version, contentType, archiveBuffer, + useStreaming, }: { - name: string; - version: string; contentType: string; archiveBuffer: Buffer; -}): Promise<{ paths: string[]; assetsMap: AssetsMap }> { - const assetsMap = new Map(); - const paths: string[] = []; - const entries = await unpackBufferEntries(archiveBuffer, contentType); - - entries.forEach((entry) => { - const { path, buffer } = entry; - if (buffer) { - assetsMap.set(path, buffer); - paths.push(path); - } - }); - - return { assetsMap, paths }; + useStreaming: boolean | undefined; +}): Promise<{ paths: string[]; assetsMap: AssetsMap; archiveIterator: ArchiveIterator }> { + const archiveIterator = createArchiveIterator(archiveBuffer, contentType); + let paths: string[] = []; + let assetsMap: AssetsMap = new Map(); + if (useStreaming) { + paths = await archiveIterator.getPaths(); + // We keep the assetsMap empty as we don't want to load all the assets in memory + assetsMap = new Map(); + } else { + const entries = await unpackArchiveEntriesIntoMemory(archiveBuffer, contentType); + + entries.forEach((entry) => { + const { path, buffer } = entry; + if (buffer) { + assetsMap.set(path, buffer); + paths.push(path); + } + }); + } + + return { paths, assetsMap, archiveIterator }; } -export async function unpackBufferEntries( +/** + * This function extracts all archive entries into memory. + * + * NOTE: This is potentially dangerous for large archives and can cause OOM + * errors. Use 'traverseArchiveEntries' instead to iterate over the entries + * without storing them all in memory at once. + * + * @param archiveBuffer + * @param contentType + * @returns All the entries in the archive buffer + */ +export async function unpackArchiveEntriesIntoMemory( archiveBuffer: Buffer, contentType: string ): Promise { + const entries: ArchiveEntry[] = []; + const addToEntries = async (entry: ArchiveEntry) => void entries.push(entry); + await traverseArchiveEntries(archiveBuffer, contentType, addToEntries); + + // While unpacking a tar.gz file with unzipBuffer() will result in a thrown + // error, unpacking a zip file with untarBuffer() just results in nothing. + if (entries.length === 0) { + throw new PackageInvalidArchiveError( + `Archive seems empty. Assumed content type was ${contentType}, check if this matches the archive type.` + ); + } + return entries; +} + +export async function traverseArchiveEntries( + archiveBuffer: Buffer, + contentType: string, + onEntry: (entry: ArchiveEntry) => Promise +) { const bufferExtractor = getBufferExtractor({ contentType }); if (!bufferExtractor) { throw new PackageUnsupportedMediaTypeError( `Unsupported media type ${contentType}. Please use 'application/gzip' or 'application/zip'` ); } - const entries: ArchiveEntry[] = []; try { const onlyFiles = ({ path }: ArchiveEntry): boolean => !path.endsWith('/'); - const addToEntries = (entry: ArchiveEntry) => entries.push(entry); - await bufferExtractor(archiveBuffer, onlyFiles, addToEntries); + await bufferExtractor(archiveBuffer, onlyFiles, onEntry); } catch (error) { throw new PackageInvalidArchiveError( `Error during extraction of package: ${error}. Assumed content type was ${contentType}, check if this matches the archive type.` ); } - - // While unpacking a tar.gz file with unzipBuffer() will result in a thrown error in the try-catch above, - // unpacking a zip file with untarBuffer() just results in nothing. - if (entries.length === 0) { - throw new PackageInvalidArchiveError( - `Archive seems empty. Assumed content type was ${contentType}, check if this matches the archive type.` - ); - } - return entries; } export const deletePackageCache = ({ name, version }: SharedKey) => { diff --git a/x-pack/plugins/fleet/server/services/epm/archive/parse.ts b/x-pack/plugins/fleet/server/services/epm/archive/parse.ts index 530ca804f24eb..8cccfe9982457 100644 --- a/x-pack/plugins/fleet/server/services/epm/archive/parse.ts +++ b/x-pack/plugins/fleet/server/services/epm/archive/parse.ts @@ -40,7 +40,7 @@ import { import { PackageInvalidArchiveError } from '../../../errors'; import { pkgToPkgKey } from '../registry'; -import { unpackBufferEntries } from '.'; +import { traverseArchiveEntries } from '.'; const readFileAsync = promisify(readFile); export const MANIFEST_NAME = 'manifest.yml'; @@ -160,9 +160,8 @@ export async function generatePackageInfoFromArchiveBuffer( contentType: string ): Promise<{ paths: string[]; packageInfo: ArchivePackage }> { const assetsMap: AssetsBufferMap = {}; - const entries = await unpackBufferEntries(archiveBuffer, contentType); const paths: string[] = []; - entries.forEach(({ path: bufferPath, buffer }) => { + await traverseArchiveEntries(archiveBuffer, contentType, async ({ path: bufferPath, buffer }) => { paths.push(bufferPath); if (buffer && filterAssetPathForParseAndVerifyArchive(bufferPath)) { assetsMap[bufferPath] = buffer; 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 dd6321445df75..8f6f151383d5a 100644 --- a/x-pack/plugins/fleet/server/services/epm/archive/storage.ts +++ b/x-pack/plugins/fleet/server/services/epm/archive/storage.ts @@ -15,6 +15,7 @@ import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import { ASSETS_SAVED_OBJECT_TYPE } from '../../../../common'; import type { + ArchiveEntry, InstallablePackage, InstallSource, PackageAssetReference, @@ -24,7 +25,6 @@ import { PackageInvalidArchiveError, PackageNotFoundError } from '../../../error import { appContextService } from '../../app_context'; import { setPackageInfo } from '.'; -import type { ArchiveEntry } from '.'; import { filterAssetPathForParseAndVerifyArchive, parseAndVerifyArchive } from './parse'; const ONE_BYTE = 1024 * 1024; diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/install.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/install.ts index a456734747324..5a4672f67fe53 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/install.ts @@ -16,14 +16,13 @@ import type { PackageInfo, } from '../../../../types'; import { getAssetFromAssetsMap, getPathParts } from '../../archive'; -import type { ArchiveEntry } from '../../archive'; import { FLEET_FINAL_PIPELINE_CONTENT, FLEET_FINAL_PIPELINE_ID, FLEET_FINAL_PIPELINE_VERSION, } from '../../../../constants'; import { getPipelineNameForDatastream } from '../../../../../common/services'; -import type { PackageInstallContext } from '../../../../../common/types'; +import type { ArchiveEntry, PackageInstallContext } from '../../../../../common/types'; import { appendMetadataToIngestPipeline } from '../meta'; import { retryTransientEsErrors } from '../retry'; diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/mappings.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/mappings.test.ts index f34015bf77697..de962850fba8c 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/mappings.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/mappings.test.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { createArchiveIteratorFromMap } from '../../archive/archive_iterator'; + import { loadMappingForTransform } from './mappings'; describe('loadMappingForTransform', () => { @@ -13,6 +15,7 @@ describe('loadMappingForTransform', () => { { packageInfo: {} as any, assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], }, 'test' @@ -49,6 +52,7 @@ describe('loadMappingForTransform', () => { ), ], ]), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [ '/package/ti_opencti/2.1.0/elasticsearch/transform/latest_ioc/fields/ecs.yml', '/package/ti_opencti/2.1.0/elasticsearch/transform/latest_ioc/fields/ecs-extra.yml', 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 276478099daf8..bf5684f29c205 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 @@ -325,16 +325,16 @@ export async function deleteKibanaAssetsAndReferencesForSpace({ await saveKibanaAssetsRefs(savedObjectsClient, pkgName, [], true); } +const kibanaAssetTypes = Object.values(KibanaAssetType); +export const isKibanaAssetType = (path: string) => { + const parts = getPathParts(path); + + return parts.service === 'kibana' && (kibanaAssetTypes as string[]).includes(parts.type); +}; + export function getKibanaAssets( packageInstallContext: PackageInstallContext ): Record { - const kibanaAssetTypes = Object.values(KibanaAssetType); - const isKibanaAssetType = (path: string) => { - const parts = getPathParts(path); - - return parts.service === 'kibana' && (kibanaAssetTypes as string[]).includes(parts.type); - }; - const result = Object.fromEntries( kibanaAssetTypes.map((type) => [type, []]) ) as Record; diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install_with_streaming.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install_with_streaming.ts new file mode 100644 index 0000000000000..fca6cf27a0cd7 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install_with_streaming.ts @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SavedObject, SavedObjectsClientContract } from '@kbn/core/server'; + +import type { Installation, PackageInstallContext } from '../../../../../common/types'; +import type { KibanaAssetReference, KibanaAssetType } from '../../../../types'; +import { getPathParts } from '../../archive'; + +import { saveKibanaAssetsRefs } from '../../packages/install'; + +import type { ArchiveAsset } from './install'; +import { + KibanaSavedObjectTypeMapping, + createSavedObjectKibanaAsset, + isKibanaAssetType, + toAssetReference, +} from './install'; +import { getSpaceAwareSaveobjectsClients } from './saved_objects'; + +interface InstallKibanaAssetsWithStreamingArgs { + pkgName: string; + packageInstallContext: PackageInstallContext; + spaceId: string; + savedObjectsClient: SavedObjectsClientContract; + installedPkg?: SavedObject | undefined; +} + +const MAX_ASSETS_TO_INSTALL_IN_PARALLEL = 100; + +export async function installKibanaAssetsWithStreaming({ + spaceId, + packageInstallContext, + savedObjectsClient, + pkgName, + installedPkg, +}: InstallKibanaAssetsWithStreamingArgs): Promise { + const { archiveIterator } = packageInstallContext; + + const { savedObjectClientWithSpace } = getSpaceAwareSaveobjectsClients(spaceId); + + const assetRefs: KibanaAssetReference[] = []; + let batch: ArchiveAsset[] = []; + + await archiveIterator.traverseEntries(async ({ path, buffer }) => { + if (!buffer || !isKibanaAssetType(path)) { + return; + } + const savedObject = JSON.parse(buffer.toString('utf8')) as ArchiveAsset; + const assetType = getPathParts(path).type as KibanaAssetType; + const soType = KibanaSavedObjectTypeMapping[assetType]; + if (savedObject.type !== soType) { + return; + } + + batch.push(savedObject); + assetRefs.push(toAssetReference(savedObject)); + + if (batch.length >= MAX_ASSETS_TO_INSTALL_IN_PARALLEL) { + await bulkCreateSavedObjects({ + savedObjectsClient: savedObjectClientWithSpace, + kibanaAssets: batch, + refresh: false, + }); + batch = []; + } + }); + + // install any remaining assets + if (batch.length) { + await bulkCreateSavedObjects({ + savedObjectsClient: savedObjectClientWithSpace, + kibanaAssets: batch, + // Use wait_for with the last batch to ensure all assets are readable once the install is complete + refresh: 'wait_for', + }); + } + + // Update the installation saved object with installed kibana assets + await saveKibanaAssetsRefs(savedObjectsClient, pkgName, assetRefs); + + return assetRefs; +} + +async function bulkCreateSavedObjects({ + savedObjectsClient, + kibanaAssets, + refresh, +}: { + kibanaAssets: ArchiveAsset[]; + savedObjectsClient: SavedObjectsClientContract; + refresh?: boolean | 'wait_for'; +}) { + if (!kibanaAssets.length) { + return []; + } + + const toBeSavedObjects = kibanaAssets.map((asset) => createSavedObjectKibanaAsset(asset)); + + const { saved_objects: createdSavedObjects } = await savedObjectsClient.bulkCreate( + toBeSavedObjects, + { + // We only want to install new saved objects without overwriting existing ones + overwrite: false, + managed: true, + refresh, + } + ); + + return createdSavedObjects; +} 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 661475dfadc09..a097db584b460 100644 --- a/x-pack/plugins/fleet/server/services/epm/package_service.ts +++ b/x-pack/plugins/fleet/server/services/epm/package_service.ts @@ -39,7 +39,10 @@ import type { InstallResult } from '../../../common'; import { appContextService } from '..'; -import type { CustomPackageDatasetConfiguration, EnsurePackageResult } from './packages/install'; +import { + type CustomPackageDatasetConfiguration, + type EnsurePackageResult, +} from './packages/install'; import type { FetchFindLatestPackageOptions } from './registry'; import { getPackageFieldsMetadata } from './registry'; @@ -56,6 +59,7 @@ import { } from './packages'; import { generatePackageInfoFromArchiveBuffer } from './archive'; import { getEsPackage } from './archive/storage'; +import { createArchiveIteratorFromMap } from './archive/archive_iterator'; export type InstalledAssetType = EsAssetReference; @@ -381,12 +385,14 @@ class PackageClientImpl implements PackageClient { } const { assetsMap } = esPackage; + const archiveIterator = createArchiveIteratorFromMap(assetsMap); const { installedTransforms } = await installTransforms({ packageInstallContext: { assetsMap, packageInfo, paths, + archiveIterator, }, esClient: this.internalEsClient, savedObjectsClient: this.internalSoClient, diff --git a/x-pack/plugins/fleet/server/services/epm/packages/assets.ts b/x-pack/plugins/fleet/server/services/epm/packages/assets.ts index a82b5c0d103b2..3bb84c0d23163 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/assets.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/assets.ts @@ -5,9 +5,9 @@ * 2.0. */ +import type { ArchiveEntry } from '../../../../common/types'; import type { AssetsMap, PackageInfo } from '../../../types'; import { getAssetFromAssetsMap } from '../archive'; -import type { ArchiveEntry } from '../archive'; const maybeFilterByDataset = (packageInfo: Pick, datasetName: string) => diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/get.test.ts index 2dc295762e33a..5711c8fcccaf4 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/get.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/get.test.ts @@ -27,6 +27,8 @@ import { auditLoggingService } from '../../audit_logging'; import * as Registry from '../registry'; +import { createArchiveIteratorFromMap } from '../archive/archive_iterator'; + import { getInstalledPackages, getPackageInfo, getPackages, getPackageUsageStats } from './get'; jest.mock('../registry'); @@ -915,6 +917,7 @@ owner: elastic`, MockRegistry.getPackage.mockResolvedValue({ paths: [], assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), packageInfo: { name: 'my-package', version: '1.0.0', 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 709e0d84d70fc..6b3a31eda649e 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 @@ -442,6 +442,24 @@ describe('install', () => { expect(response.status).toEqual('installed'); }); + + it('should use streaming installation for the detection rules package', async () => { + jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true); + + const response = await installPackage({ + spaceId: DEFAULT_SPACE_ID, + installSource: 'registry', + pkgkey: 'security_detection_engine', + savedObjectsClient: savedObjectsClientMock.create(), + esClient: {} as ElasticsearchClient, + }); + + expect(response.error).toBeUndefined(); + + expect(installStateMachine._stateMachineInstallPackage).toHaveBeenCalledWith( + expect.objectContaining({ useStreaming: true }) + ); + }); }); describe('upload', () => { 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 1ea6f29cad839..ebe5acc35178d 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -76,6 +76,7 @@ import { deleteVerificationResult, unpackBufferToAssetsMap, } from '../archive'; +import { createArchiveIteratorFromMap } from '../archive/archive_iterator'; import { toAssetReference } from '../kibana/assets/install'; import type { ArchiveAsset } from '../kibana/assets/install'; import type { PackageUpdateEvent } from '../../upgrade_sender'; @@ -107,6 +108,12 @@ import { removeInstallation } from './remove'; export const UPLOAD_RETRY_AFTER_MS = 10000; // 10s const MAX_ENSURE_INSTALL_TIME = 60 * 1000; +const PACKAGES_TO_INSTALL_WITH_STREAMING = [ + // The security_detection_engine package contains a large number of assets and + // is not suitable for regular installation as it might cause OOM errors. + 'security_detection_engine', +]; + export async function isPackageInstalled(options: { savedObjectsClient: SavedObjectsClientContract; pkgName: string; @@ -449,6 +456,7 @@ async function installPackageFromRegistry({ // TODO: change epm API to /packageName/version so we don't need to do this const { pkgName, pkgVersion: version } = Registry.splitPkgKey(pkgkey); let pkgVersion = version ?? ''; + const useStreaming = PACKAGES_TO_INSTALL_WITH_STREAMING.includes(pkgName); // if an error happens during getInstallType, report that we don't know let installType: InstallType = 'unknown'; @@ -478,11 +486,12 @@ async function installPackageFromRegistry({ } // get latest package version and requested version in parallel for performance - const [latestPackage, { paths, packageInfo, assetsMap, verificationResult }] = + const [latestPackage, { paths, packageInfo, assetsMap, archiveIterator, verificationResult }] = await Promise.all([ latestPkg ? Promise.resolve(latestPkg) : queryLatest(), Registry.getPackage(pkgName, pkgVersion, { ignoreUnverified: force && !neverIgnoreVerificationError, + useStreaming, }), ]); @@ -490,6 +499,7 @@ async function installPackageFromRegistry({ packageInfo, assetsMap, paths, + archiveIterator, }; // let the user install if using the force flag or needing to reinstall or install a previous version due to failed update @@ -542,6 +552,7 @@ async function installPackageFromRegistry({ ignoreMappingUpdateErrors, skipDataStreamRollover, retryFromLastState, + useStreaming, }); } catch (e) { sendEvent({ @@ -580,6 +591,7 @@ async function installPackageWithStateMachine(options: { ignoreMappingUpdateErrors?: boolean; skipDataStreamRollover?: boolean; retryFromLastState?: boolean; + useStreaming?: boolean; }): Promise { const packageInfo = options.packageInstallContext.packageInfo; @@ -599,6 +611,7 @@ async function installPackageWithStateMachine(options: { skipDataStreamRollover, packageInstallContext, retryFromLastState, + useStreaming, } = options; let { telemetryEvent } = options; const logger = appContextService.getLogger(); @@ -696,6 +709,7 @@ async function installPackageWithStateMachine(options: { ignoreMappingUpdateErrors, skipDataStreamRollover, retryFromLastState, + useStreaming, }) .then(async (assets) => { logger.debug(`Removing old assets from previous versions of ${pkgName}`); @@ -785,6 +799,7 @@ async function installPackageByUpload({ } const { packageInfo } = await generatePackageInfoFromArchiveBuffer(archiveBuffer, contentType); const pkgName = packageInfo.name; + const useStreaming = PACKAGES_TO_INSTALL_WITH_STREAMING.includes(pkgName); // Allow for overriding the version in the manifest for cases where we install // stack-aligned bundled packages to support special cases around the @@ -807,17 +822,17 @@ async function installPackageByUpload({ packageInfo, }); - const { assetsMap, paths } = await unpackBufferToAssetsMap({ - name: packageInfo.name, - version: pkgVersion, + const { paths, assetsMap, archiveIterator } = await unpackBufferToAssetsMap({ archiveBuffer, contentType, + useStreaming, }); const packageInstallContext: PackageInstallContext = { packageInfo: { ...packageInfo, version: pkgVersion }, assetsMap, paths, + archiveIterator, }; // update the timestamp of latest installation setLastUploadInstallCache(); @@ -837,6 +852,7 @@ async function installPackageByUpload({ authorizationHeader, ignoreMappingUpdateErrors, skipDataStreamRollover, + useStreaming, }); } catch (e) { return { @@ -1004,12 +1020,14 @@ export async function installCustomPackage( acc.set(asset.path, asset.content); return acc; }, new Map()); - const paths = [...assetsMap.keys()]; + const paths = assets.map((asset) => asset.path); + const archiveIterator = createArchiveIteratorFromMap(assetsMap); const packageInstallContext: PackageInstallContext = { assetsMap, paths, packageInfo, + archiveIterator, }; return await installPackageWithStateMachine({ packageInstallContext, @@ -1341,16 +1359,20 @@ export async function installAssetsForInputPackagePolicy(opts: { ignoreUnverified: force, }); + const archiveIterator = createArchiveIteratorFromMap(pkg.assetsMap); packageInstallContext = { assetsMap: pkg.assetsMap, packageInfo: pkg.packageInfo, paths: pkg.paths, + archiveIterator, }; } else { + const archiveIterator = createArchiveIteratorFromMap(installedPkgWithAssets.assetsMap); packageInstallContext = { assetsMap: installedPkgWithAssets.assetsMap, packageInfo: installedPkgWithAssets.packageInfo, paths: installedPkgWithAssets.paths, + archiveIterator, }; } diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/_state_machine_package_install.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/_state_machine_package_install.test.ts index 174076a9e9b1b..73b78a6cc4aa0 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/_state_machine_package_install.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/_state_machine_package_install.test.ts @@ -38,6 +38,8 @@ import { updateCurrentWriteIndices } from '../../elasticsearch/template/template import { installIndexTemplatesAndPipelines } from '../install_index_template_pipeline'; +import { createArchiveIteratorFromMap } from '../../archive/archive_iterator'; + import { handleState } from './state_machine'; import { _stateMachineInstallPackage } from './_state_machine_package_install'; import { cleanupLatestExecutedState } from './steps'; @@ -110,6 +112,7 @@ describe('_stateMachineInstallPackage', () => { logger: loggerMock.create(), packageInstallContext: { assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], packageInfo: { title: 'title', @@ -172,6 +175,7 @@ describe('_stateMachineInstallPackage', () => { logger: loggerMock.create(), packageInstallContext: { assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], packageInfo: { title: 'title', @@ -208,6 +212,7 @@ describe('_stateMachineInstallPackage', () => { logger: loggerMock.create(), packageInstallContext: { assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], packageInfo: { title: 'title', @@ -257,6 +262,7 @@ describe('_stateMachineInstallPackage', () => { logger: loggerMock.create(), packageInstallContext: { assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], packageInfo: { title: 'title', @@ -336,6 +342,7 @@ describe('_stateMachineInstallPackage', () => { owner: { github: 'elastic/fleet' }, } as any, assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], }, installType: 'install', diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/_state_machine_package_install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/_state_machine_package_install.ts index 1f10d40feba38..c941b6d60d63b 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/_state_machine_package_install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/_state_machine_package_install.ts @@ -48,11 +48,13 @@ import { updateLatestExecutedState, cleanupLatestExecutedState, cleanUpKibanaAssetsStep, + cleanUpUnusedKibanaAssetsStep, cleanupILMPoliciesStep, cleanUpMlModelStep, cleanupIndexTemplatePipelinesStep, cleanupTransformsStep, cleanupArchiveEntriesStep, + stepInstallKibanaAssetsWithStreaming, } from './steps'; import type { StateMachineDefinition, StateMachineStates } from './state_machine'; import { handleState } from './state_machine'; @@ -73,6 +75,7 @@ export interface InstallContext extends StateContext { skipDataStreamRollover?: boolean; retryFromLastState?: boolean; initialState?: INSTALL_STATES; + useStreaming?: boolean; indexTemplates?: IndexTemplateEntry[]; packageAssetRefs?: PackageAssetReference[]; @@ -83,7 +86,7 @@ export interface InstallContext extends StateContext { /** * This data structure defines the sequence of the states and the transitions */ -const statesDefinition: StateMachineStates = { +const regularStatesDefinition: StateMachineStates = { create_restart_installation: { nextState: INSTALL_STATES.INSTALL_KIBANA_ASSETS, onTransition: stepCreateRestartInstallation, @@ -152,6 +155,31 @@ const statesDefinition: StateMachineStates = { }, }; +const streamingStatesDefinition: StateMachineStates = { + create_restart_installation: { + nextState: INSTALL_STATES.INSTALL_KIBANA_ASSETS, + onTransition: stepCreateRestartInstallation, + onPostTransition: updateLatestExecutedState, + }, + install_kibana_assets: { + onTransition: stepInstallKibanaAssetsWithStreaming, + nextState: INSTALL_STATES.SAVE_ARCHIVE_ENTRIES, + onPostTransition: updateLatestExecutedState, + }, + save_archive_entries_from_assets_map: { + onPreTransition: cleanupArchiveEntriesStep, + onTransition: stepSaveArchiveEntries, + nextState: INSTALL_STATES.UPDATE_SO, + onPostTransition: updateLatestExecutedState, + }, + update_so: { + onPreTransition: cleanUpUnusedKibanaAssetsStep, + onTransition: stepSaveSystemObject, + nextState: 'end', + onPostTransition: updateLatestExecutedState, + }, +}; + /* * _stateMachineInstallPackage installs packages using the generic state machine in ./state_machine * installStates is the data structure providing the state machine definition @@ -166,6 +194,10 @@ export async function _stateMachineInstallPackage( const logger = appContextService.getLogger(); let initialState = INSTALL_STATES.CREATE_RESTART_INSTALLATION; + const statesDefinition = context.useStreaming + ? streamingStatesDefinition + : regularStatesDefinition; + // if retryFromLastState, restart install from last install state // if force is passed, the install should be executed from the beginning if (retryFromLastState && !force && installedPkg?.attributes?.latest_executed_state?.name) { diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_create_restart_installation.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_create_restart_installation.test.ts index 2b653728d6574..e5a7fed55fe87 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_create_restart_installation.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_create_restart_installation.test.ts @@ -31,6 +31,8 @@ import { auditLoggingService } from '../../../../audit_logging'; import { restartInstallation, createInstallation } from '../../install'; import type { Installation } from '../../../../../../common'; +import { createArchiveIteratorFromMap } from '../../../archive/archive_iterator'; + import { stepCreateRestartInstallation } from './step_create_restart_installation'; jest.mock('../../../../audit_logging'); @@ -84,6 +86,7 @@ describe('stepCreateRestartInstallation', () => { logger, packageInstallContext: { assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], packageInfo: { title: 'title', @@ -120,6 +123,7 @@ describe('stepCreateRestartInstallation', () => { logger, packageInstallContext: { assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], packageInfo: { title: 'title', @@ -164,6 +168,7 @@ describe('stepCreateRestartInstallation', () => { logger, packageInstallContext: { assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], packageInfo: { title: 'title', @@ -208,6 +213,7 @@ describe('stepCreateRestartInstallation', () => { logger, packageInstallContext: { assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], packageInfo: { title: 'title', diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_delete_previous_pipelines.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_delete_previous_pipelines.test.ts index 7d8a251433bb5..06201770ee2e2 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_delete_previous_pipelines.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_delete_previous_pipelines.test.ts @@ -24,6 +24,8 @@ import { deletePreviousPipelines, } from '../../../elasticsearch/ingest_pipeline'; +import { createArchiveIteratorFromMap } from '../../../archive/archive_iterator'; + import { stepDeletePreviousPipelines } from './step_delete_previous_pipelines'; jest.mock('../../../elasticsearch/ingest_pipeline'); @@ -84,6 +86,7 @@ describe('stepDeletePreviousPipelines', () => { owner: { github: 'elastic/fleet' }, } as any, assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], }; appContextService.start( @@ -276,6 +279,7 @@ describe('stepDeletePreviousPipelines', () => { owner: { github: 'elastic/fleet' }, } as any, assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], }; appContextService.start( diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_ilm_policies.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_ilm_policies.test.ts index 2cf9b23bb9adb..4c106a0c68f15 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_ilm_policies.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_ilm_policies.test.ts @@ -24,6 +24,8 @@ import { installIlmForDataStream } from '../../../elasticsearch/datastream_ilm/i import { ElasticsearchAssetType } from '../../../../../types'; import { deleteILMPolicies, deletePrerequisiteAssets } from '../../remove'; +import { createArchiveIteratorFromMap } from '../../../archive/archive_iterator'; + import { stepInstallILMPolicies, cleanupILMPoliciesStep } from './step_install_ilm_policies'; jest.mock('../../../archive/storage'); @@ -56,6 +58,7 @@ const packageInstallContext = { owner: { github: 'elastic/fleet' }, } as any, assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], }; let soClient: jest.Mocked; @@ -239,6 +242,7 @@ describe('stepInstallILMPolicies', () => { owner: { github: 'elastic/fleet' }, } as any, assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], }, installType: 'install', diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_index_template_pipelines.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_index_template_pipelines.test.ts index d258747edc6ef..1c368cfd998d3 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_index_template_pipelines.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_index_template_pipelines.test.ts @@ -37,6 +37,8 @@ const mockDeletePrerequisiteAssets = deletePrerequisiteAssets as jest.MockedFunc typeof deletePrerequisiteAssets >; +import { createArchiveIteratorFromMap } from '../../../archive/archive_iterator'; + import { stepInstallIndexTemplatePipelines, cleanupIndexTemplatePipelinesStep, @@ -122,6 +124,7 @@ describe('stepInstallIndexTemplatePipelines', () => { owner: { github: 'elastic/fleet' }, } as any, assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], }; appContextService.start( @@ -281,6 +284,7 @@ describe('stepInstallIndexTemplatePipelines', () => { ], } as any, assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], }; appContextService.start( @@ -431,6 +435,7 @@ describe('stepInstallIndexTemplatePipelines', () => { ], } as any, assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], }; appContextService.start( @@ -521,6 +526,7 @@ describe('stepInstallIndexTemplatePipelines', () => { ], } as any, assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], }; appContextService.start( @@ -574,6 +580,7 @@ describe('stepInstallIndexTemplatePipelines', () => { owner: { github: 'elastic/fleet' }, } as any, assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], }; appContextService.start( @@ -647,6 +654,7 @@ describe('cleanupIndexTemplatePipelinesStep', () => { ], } as any, assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], }; const mockInstalledPackageSo: SavedObject = { diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_kibana_assets.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_kibana_assets.test.ts index 52c93c61c16e1..cf9d953868b6a 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_kibana_assets.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_kibana_assets.test.ts @@ -23,8 +23,25 @@ import { deleteKibanaAssets } from '../../remove'; import { KibanaSavedObjectType, type Installation } from '../../../../../types'; -import { stepInstallKibanaAssets, cleanUpKibanaAssetsStep } from './step_install_kibana_assets'; +import { createArchiveIteratorFromMap } from '../../../archive/archive_iterator'; +import { + stepInstallKibanaAssets, + cleanUpKibanaAssetsStep, + stepInstallKibanaAssetsWithStreaming, + cleanUpUnusedKibanaAssetsStep, +} from './step_install_kibana_assets'; + +jest.mock('../../../kibana/assets/saved_objects', () => { + return { + getSpaceAwareSaveobjectsClients: jest.fn().mockReturnValue({ + savedObjectClientWithSpace: jest.fn(), + savedObjectsImporter: jest.fn(), + savedObjectTagAssignmentService: jest.fn(), + savedObjectTagClient: jest.fn(), + }), + }; +}); jest.mock('../../../kibana/assets/install'); jest.mock('../../remove', () => { return { @@ -58,6 +75,7 @@ const packageInstallContext = { } as any, paths: ['some/path/1', 'some/path/2'], assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), }; describe('stepInstallKibanaAssets', () => { @@ -82,6 +100,7 @@ describe('stepInstallKibanaAssets', () => { logger: loggerMock.create(), packageInstallContext: { assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], packageInfo: { title: 'title', @@ -102,7 +121,7 @@ describe('stepInstallKibanaAssets', () => { }); await expect(installationPromise).resolves.not.toThrowError(); - expect(mockedInstallKibanaAssetsAndReferencesMultispace).toBeCalledTimes(1); + expect(installKibanaAssetsAndReferencesMultispace).toBeCalledTimes(1); }); esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; appContextService.start(createAppContextStartContractMock()); @@ -121,6 +140,7 @@ describe('stepInstallKibanaAssets', () => { logger: loggerMock.create(), packageInstallContext: { assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], packageInfo: { title: 'title', @@ -144,6 +164,60 @@ describe('stepInstallKibanaAssets', () => { }); }); +describe('stepInstallKibanaAssetsWithStreaming', () => { + beforeEach(async () => { + soClient = savedObjectsClientMock.create(); + esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + appContextService.start(createAppContextStartContractMock()); + }); + + it('should rely on archiveIterator instead of in-memory assetsMap', async () => { + const assetsMap = new Map(); + assetsMap.get = jest.fn(); + assetsMap.set = jest.fn(); + + const archiveIterator = { + traverseEntries: jest.fn(), + getPaths: jest.fn(), + }; + + const result = await stepInstallKibanaAssetsWithStreaming({ + savedObjectsClient: soClient, + esClient, + logger: loggerMock.create(), + packageInstallContext: { + assetsMap, + archiveIterator, + paths: [], + packageInfo: { + title: 'title', + name: 'xyz', + version: '4.5.6', + description: 'test', + type: 'integration', + categories: ['cloud', 'custom'], + format_version: 'string', + release: 'experimental', + conditions: { kibana: { version: 'x.y.z' } }, + owner: { github: 'elastic/fleet' }, + }, + }, + installType: 'install', + installSource: 'registry', + spaceId: DEFAULT_SPACE_ID, + }); + + expect(result).toEqual({ installedKibanaAssetsRefs: [] }); + + // Verify that assetsMap was not used + expect(assetsMap.get).not.toBeCalled(); + expect(assetsMap.set).not.toBeCalled(); + + // Verify that archiveIterator was used + expect(archiveIterator.traverseEntries).toBeCalled(); + }); +}); + describe('cleanUpKibanaAssetsStep', () => { const mockInstalledPackageSo: SavedObject = { id: 'mocked-package', @@ -302,3 +376,84 @@ describe('cleanUpKibanaAssetsStep', () => { expect(mockedDeleteKibanaAssets).not.toBeCalled(); }); }); + +describe('cleanUpUnusedKibanaAssetsStep', () => { + const mockInstalledPackageSo: SavedObject = { + id: 'mocked-package', + attributes: { + name: 'test-package', + version: '1.0.0', + install_status: 'installing', + install_version: '1.0.0', + install_started_at: new Date().toISOString(), + install_source: 'registry', + verification_status: 'verified', + installed_kibana: [] as any, + installed_es: [] as any, + es_index_patterns: {}, + }, + type: PACKAGES_SAVED_OBJECT_TYPE, + references: [], + }; + + const installationContext = { + savedObjectsClient: soClient, + savedObjectsImporter: jest.fn(), + esClient, + logger: loggerMock.create(), + packageInstallContext, + installType: 'install' as const, + installSource: 'registry' as const, + spaceId: DEFAULT_SPACE_ID, + retryFromLastState: true, + initialState: 'install_kibana_assets' as any, + }; + + beforeEach(async () => { + soClient = savedObjectsClientMock.create(); + esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + appContextService.start(createAppContextStartContractMock()); + }); + + it('should not clean up assets if they all present in the new package', async () => { + const installedAssets = [{ type: KibanaSavedObjectType.dashboard, id: 'dashboard-1' }]; + await cleanUpUnusedKibanaAssetsStep({ + ...installationContext, + installedPkg: { + ...mockInstalledPackageSo, + attributes: { + ...mockInstalledPackageSo.attributes, + installed_kibana: installedAssets, + }, + }, + installedKibanaAssetsRefs: installedAssets, + }); + + expect(mockedDeleteKibanaAssets).not.toBeCalled(); + }); + + it('should clean up assets that are not present in the new package', async () => { + const installedAssets = [ + { type: KibanaSavedObjectType.dashboard, id: 'dashboard-1' }, + { type: KibanaSavedObjectType.dashboard, id: 'dashboard-2' }, + ]; + const newAssets = [{ type: KibanaSavedObjectType.dashboard, id: 'dashboard-1' }]; + await cleanUpUnusedKibanaAssetsStep({ + ...installationContext, + installedPkg: { + ...mockInstalledPackageSo, + attributes: { + ...mockInstalledPackageSo.attributes, + installed_kibana: installedAssets, + }, + }, + installedKibanaAssetsRefs: newAssets, + }); + + expect(mockedDeleteKibanaAssets).toBeCalledWith({ + installedObjects: [installedAssets[1]], + spaceId: 'default', + packageInfo: packageInstallContext.packageInfo, + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_kibana_assets.ts b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_kibana_assets.ts index b5a1fff91d3b8..aabd23f2eb9cc 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_kibana_assets.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_kibana_assets.ts @@ -11,7 +11,9 @@ import { withPackageSpan } from '../../utils'; import type { InstallContext } from '../_state_machine_package_install'; import { deleteKibanaAssets } from '../../remove'; +import type { KibanaAssetReference } from '../../../../../../common/types'; import { INSTALL_STATES } from '../../../../../../common/types'; +import { installKibanaAssetsWithStreaming } from '../../../kibana/assets/install_with_streaming'; export async function stepInstallKibanaAssets(context: InstallContext) { const { savedObjectsClient, logger, installedPkg, packageInstallContext, spaceId } = context; @@ -37,6 +39,26 @@ export async function stepInstallKibanaAssets(context: InstallContext) { return { kibanaAssetPromise }; } +export async function stepInstallKibanaAssetsWithStreaming(context: InstallContext) { + const { savedObjectsClient, installedPkg, packageInstallContext, spaceId } = context; + const { packageInfo } = packageInstallContext; + const { name: pkgName } = packageInfo; + + const installedKibanaAssetsRefs = await withPackageSpan( + 'Install Kibana assets with streaming', + () => + installKibanaAssetsWithStreaming({ + savedObjectsClient, + pkgName, + packageInstallContext, + installedPkg, + spaceId, + }) + ); + + return { installedKibanaAssetsRefs }; +} + export async function cleanUpKibanaAssetsStep(context: InstallContext) { const { logger, @@ -65,3 +87,44 @@ export async function cleanUpKibanaAssetsStep(context: InstallContext) { }); } } + +/** + * Cleans up Kibana assets that are no longer in the package. As opposite to + * `cleanUpKibanaAssetsStep`, this one is used after the package assets are + * installed. + * + * This function compares the currently installed Kibana assets with the assets + * in the previous package and removes any assets that are no longer present in the + * new installation. + * + */ +export async function cleanUpUnusedKibanaAssetsStep(context: InstallContext) { + const { logger, installedPkg, packageInstallContext, spaceId, installedKibanaAssetsRefs } = + context; + const { packageInfo } = packageInstallContext; + + if (!installedKibanaAssetsRefs) { + return; + } + + logger.debug('Clean up Kibana assets that are no longer in the package'); + + // Get the assets installed by the previous package + const previousAssetRefs = installedPkg?.attributes.installed_kibana ?? []; + + // Remove any assets that are not in the new package + const nextAssetRefKeys = new Set( + installedKibanaAssetsRefs.map((asset: KibanaAssetReference) => `${asset.id}-${asset.type}`) + ); + const assetsToRemove = previousAssetRefs.filter( + (existingAsset) => !nextAssetRefKeys.has(`${existingAsset.id}-${existingAsset.type}`) + ); + + if (assetsToRemove.length === 0) { + return; + } + + await withPackageSpan('Clean up Kibana assets that are no longer in the package', async () => { + await deleteKibanaAssets({ installedObjects: assetsToRemove, spaceId, packageInfo }); + }); +} diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_mlmodel.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_mlmodel.test.ts index 1afb436eb4361..df939f3a458b6 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_mlmodel.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_mlmodel.test.ts @@ -22,6 +22,8 @@ import { createAppContextStartContractMock } from '../../../../../mocks'; import { installMlModel } from '../../../elasticsearch/ml_model'; import { deleteMLModels, deletePrerequisiteAssets } from '../../remove'; +import { createArchiveIteratorFromMap } from '../../../archive/archive_iterator'; + import { stepInstallMlModel, cleanUpMlModelStep } from './step_install_mlmodel'; jest.mock('../../../elasticsearch/ml_model'); @@ -53,6 +55,7 @@ const packageInstallContext = { } as any, paths: ['some/path/1', 'some/path/2'], assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), }; let soClient: jest.Mocked; let esClient: jest.Mocked; diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_transforms.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_transforms.test.ts index 1ac2383950b05..3bf07d52c6cbf 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_transforms.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_install_transforms.test.ts @@ -22,6 +22,8 @@ import { createAppContextStartContractMock } from '../../../../../mocks'; import { installTransforms } from '../../../elasticsearch/transform/install'; import { cleanupTransforms } from '../../remove'; +import { createArchiveIteratorFromMap } from '../../../archive/archive_iterator'; + import { stepInstallTransforms, cleanupTransformsStep } from './step_install_transforms'; jest.mock('../../../elasticsearch/transform/install'); @@ -52,6 +54,7 @@ const packageInstallContext = { } as any, paths: ['some/path/1', 'some/path/2'], assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), }; describe('stepInstallTransforms', () => { diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_remove_legacy_templates.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_remove_legacy_templates.test.ts index 39e7159596ba8..7fa00a1c57f57 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_remove_legacy_templates.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_remove_legacy_templates.test.ts @@ -24,6 +24,8 @@ import { appContextService } from '../../../../app_context'; import { createAppContextStartContractMock } from '../../../../../mocks'; import { removeLegacyTemplates } from '../../../elasticsearch/template/remove_legacy'; +import { createArchiveIteratorFromMap } from '../../../archive/archive_iterator'; + import { stepRemoveLegacyTemplates } from './step_remove_legacy_templates'; jest.mock('../../../elasticsearch/template/remove_legacy'); @@ -82,6 +84,7 @@ describe('stepRemoveLegacyTemplates', () => { } as any, paths: ['some/path/1', 'some/path/2'], assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), }; appContextService.start( createAppContextStartContractMock({ diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_save_archive_entries.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_save_archive_entries.test.ts index b03c146640488..255572d57cf49 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_save_archive_entries.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_save_archive_entries.test.ts @@ -21,6 +21,8 @@ import { appContextService } from '../../../../app_context'; import { createAppContextStartContractMock } from '../../../../../mocks'; import { saveArchiveEntriesFromAssetsMap, removeArchiveEntries } from '../../../archive/storage'; +import { createArchiveIteratorFromMap } from '../../../archive/archive_iterator'; + import { stepSaveArchiveEntries, cleanupArchiveEntriesStep } from './step_save_archive_entries'; jest.mock('../../../archive/storage', () => { @@ -60,6 +62,7 @@ const packageInstallContext = { Buffer.from('{"content": "data"}'), ], ]), + archiveIterator: createArchiveIteratorFromMap(new Map()), }; const getMockInstalledPackageSo = ( installedEs: EsAssetReference[] = [] diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_save_archive_entries.ts b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_save_archive_entries.ts index b0d5bb67627a6..7db44bb243f85 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_save_archive_entries.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_save_archive_entries.ts @@ -14,17 +14,32 @@ import { withPackageSpan } from '../../utils'; import type { InstallContext } from '../_state_machine_package_install'; import { INSTALL_STATES } from '../../../../../../common/types'; +import { MANIFEST_NAME } from '../../../archive/parse'; export async function stepSaveArchiveEntries(context: InstallContext) { - const { packageInstallContext, savedObjectsClient, installSource } = context; + const { packageInstallContext, savedObjectsClient, installSource, useStreaming } = context; - const { packageInfo } = packageInstallContext; + const { packageInfo, archiveIterator } = packageInstallContext; + + let assetsMap = packageInstallContext?.assetsMap; + let paths = packageInstallContext?.paths; + // For stream based installations, we don't want to save any assets but + // manifest.yaml due to the large number of assets in the package. + if (useStreaming) { + assetsMap = new Map(); + await archiveIterator.traverseEntries(async (entry) => { + if (entry.path.endsWith(MANIFEST_NAME)) { + assetsMap.set(entry.path, entry.buffer); + } + }); + paths = Array.from(assetsMap.keys()); + } const packageAssetResults = await withPackageSpan('Update archive entries', () => saveArchiveEntriesFromAssetsMap({ savedObjectsClient, - assetsMap: packageInstallContext?.assetsMap, - paths: packageInstallContext?.paths, + assetsMap, + paths, packageInfo, installSource, }) diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_save_system_object.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_save_system_object.test.ts index aecdd0b2552c4..8d80c236aefb0 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_save_system_object.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_save_system_object.test.ts @@ -21,6 +21,8 @@ import { createAppContextStartContractMock } from '../../../../../mocks'; import { auditLoggingService } from '../../../../audit_logging'; import { packagePolicyService } from '../../../../package_policy'; +import { createArchiveIteratorFromMap } from '../../../archive/archive_iterator'; + import { stepSaveSystemObject } from './step_save_system_object'; jest.mock('../../../../audit_logging'); @@ -67,6 +69,7 @@ describe('updateLatestExecutedState', () => { logger, packageInstallContext: { assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], packageInfo: { title: 'title', @@ -133,6 +136,7 @@ describe('updateLatestExecutedState', () => { logger, packageInstallContext: { assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], packageInfo: { title: 'title', diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_update_current_write_indices.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_update_current_write_indices.test.ts index c7f3c040b7966..017805d34efef 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_update_current_write_indices.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_update_current_write_indices.test.ts @@ -22,6 +22,8 @@ import { appContextService } from '../../../../app_context'; import { createAppContextStartContractMock } from '../../../../../mocks'; import { updateCurrentWriteIndices } from '../../../elasticsearch/template/template'; +import { createArchiveIteratorFromMap } from '../../../archive/archive_iterator'; + import { stepUpdateCurrentWriteIndices } from './step_update_current_write_indices'; jest.mock('../../../elasticsearch/template/template'); @@ -86,6 +88,7 @@ describe('stepUpdateCurrentWriteIndices', () => { } as any, paths: ['some/path/1', 'some/path/2'], assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), }; appContextService.start( createAppContextStartContractMock({ diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/update_latest_executed_state.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/update_latest_executed_state.test.ts index d963e5fea44c9..aea879aba5479 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/update_latest_executed_state.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/update_latest_executed_state.test.ts @@ -32,6 +32,8 @@ import { auditLoggingService } from '../../../../audit_logging'; import type { PackagePolicySOAttributes } from '../../../../../types'; +import { createArchiveIteratorFromMap } from '../../../archive/archive_iterator'; + import { updateLatestExecutedState } from './update_latest_executed_state'; jest.mock('../../../../audit_logging'); @@ -61,6 +63,7 @@ describe('updateLatestExecutedState', () => { logger, packageInstallContext: { assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], packageInfo: { title: 'title', @@ -116,6 +119,7 @@ describe('updateLatestExecutedState', () => { logger, packageInstallContext: { assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], packageInfo: { title: 'title', @@ -153,6 +157,7 @@ describe('updateLatestExecutedState', () => { logger, packageInstallContext: { assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], packageInfo: { title: 'title', @@ -198,6 +203,7 @@ describe('updateLatestExecutedState', () => { logger, packageInstallContext: { assetsMap: new Map(), + archiveIterator: createArchiveIteratorFromMap(new Map()), paths: [], packageInfo: { title: 'title', 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 ac3f5def5d09c..3892eaa951e5f 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/remove.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/remove.ts @@ -148,6 +148,7 @@ export async function deleteKibanaAssets({ const namespace = SavedObjectsUtils.namespaceStringToId(spaceId); + // TODO this should be the installed package info, not the package that is being installed const minKibana = packageInfo.conditions?.kibana?.version ? minVersion(packageInfo.conditions.kibana.version) : null; 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 bb4d612aa7de3..75b9869d0a7c6 100644 --- a/x-pack/plugins/fleet/server/services/epm/registry/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/registry/index.ts @@ -54,6 +54,8 @@ import { resolveDataStreamFields, resolveDataStreamsMap, withPackageSpan } from import { verifyPackageArchiveSignature } from '../packages/package_verification'; +import type { ArchiveIterator } from '../../../../common/types'; + import { fetchUrl, getResponse, getResponseStream } from './requests'; import { getRegistryUrl } from './registry_url'; @@ -309,11 +311,12 @@ async function getPackageInfoFromArchiveOrCache( export async function getPackage( name: string, version: string, - options?: { ignoreUnverified?: boolean } + options?: { ignoreUnverified?: boolean; useStreaming?: boolean } ): Promise<{ paths: string[]; packageInfo: ArchivePackage; assetsMap: AssetsMap; + archiveIterator: ArchiveIterator; verificationResult?: PackageVerificationResult; }> { const verifyPackage = appContextService.getExperimentalFeatures().packageVerification; @@ -340,18 +343,18 @@ export async function getPackage( setVerificationResult({ name, version }, latestVerificationResult); } - const { assetsMap, paths } = await unpackBufferToAssetsMap({ - name, - version, + const contentType = ensureContentType(archivePath); + const { paths, assetsMap, archiveIterator } = await unpackBufferToAssetsMap({ archiveBuffer, - contentType: ensureContentType(archivePath), + contentType, + useStreaming: options?.useStreaming, }); if (!packageInfo) { packageInfo = await getPackageInfoFromArchiveOrCache(name, version, archiveBuffer, archivePath); } - return { paths, packageInfo, assetsMap, verificationResult }; + return { paths, packageInfo, assetsMap, archiveIterator, verificationResult }; } export async function getPackageFieldsMetadata( @@ -397,7 +400,7 @@ export async function getPackageFieldsMetadata( } } -function ensureContentType(archivePath: string) { +export function ensureContentType(archivePath: string) { const contentType = mime.lookup(archivePath); if (!contentType) { diff --git a/x-pack/plugins/fleet/server/services/package_policies/experimental_datastream_features.ts b/x-pack/plugins/fleet/server/services/package_policies/experimental_datastream_features.ts index edf31991634b9..cd1c26942aa0c 100644 --- a/x-pack/plugins/fleet/server/services/package_policies/experimental_datastream_features.ts +++ b/x-pack/plugins/fleet/server/services/package_policies/experimental_datastream_features.ts @@ -30,6 +30,7 @@ import { applyDocOnlyValueToMapping, forEachMappings, } from '../experimental_datastream_features_helper'; +import { createArchiveIteratorFromMap } from '../epm/archive/archive_iterator'; export async function handleExperimentalDatastreamFeatureOptIn({ soClient, @@ -75,6 +76,7 @@ export async function handleExperimentalDatastreamFeatureOptIn({ return prepareTemplate({ packageInstallContext: { assetsMap, + archiveIterator: createArchiveIteratorFromMap(assetsMap), packageInfo, paths, }, diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index bc5bce9eea2a3..48dc3956d6984 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -480,22 +480,21 @@ class PackagePolicyClientImpl implements PackagePolicyClient { user?: AuthenticatedUser; bumpRevision?: boolean; force?: true; + asyncDeploy?: boolean; } ): Promise<{ created: PackagePolicy[]; failed: Array<{ packagePolicy: NewPackagePolicy; error?: Error | SavedObjectError }>; }> { - const useSpaceAwareness = await isSpaceAwarenessEnabled(); - const savedObjectType = await getPackagePolicySavedObjectType(); - for (const packagePolicy of packagePolicies) { + const [useSpaceAwareness, savedObjectType, packageInfos] = await Promise.all([ + isSpaceAwarenessEnabled(), + getPackagePolicySavedObjectType(), + getPackageInfoForPackagePolicies(packagePolicies, soClient), + ]); + + await pMap(packagePolicies, async (packagePolicy) => { const basePkgInfo = packagePolicy.package - ? await getPackageInfo({ - savedObjectsClient: soClient, - pkgName: packagePolicy.package.name, - pkgVersion: packagePolicy.package.version, - ignoreUnverified: true, - prerelease: true, - }) + ? packageInfos.get(`${packagePolicy.package.name}-${packagePolicy.package.version}`) : undefined; if (!packagePolicy.id) { packagePolicy.id = SavedObjectsUtils.generateId(); @@ -508,7 +507,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { this.keepPolicyIdInSync(packagePolicy); await preflightCheckPackagePolicy(soClient, packagePolicy, basePkgInfo); - } + }); const agentPolicyIds = new Set(packagePolicies.flatMap((pkgPolicy) => pkgPolicy.policy_ids)); @@ -528,8 +527,6 @@ class PackagePolicyClientImpl implements PackagePolicyClient { } } - const packageInfos = await getPackageInfoForPackagePolicies(packagePolicies, soClient); - const isoDate = new Date().toISOString(); const policiesToCreate: Array> = []; @@ -665,6 +662,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { for (const agentPolicyId of agentPolicyIds) { await agentPolicyService.bumpRevision(soClient, esClient, agentPolicyId, { user: options?.user, + asyncDeploy: options?.asyncDeploy, }); } } @@ -1176,7 +1174,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, packagePolicyUpdates: Array, - options?: { user?: AuthenticatedUser; force?: boolean } + options?: { user?: AuthenticatedUser; force?: boolean; asyncDeploy?: boolean } ): Promise<{ updatedPolicies: PackagePolicy[] | null; failedPolicies: Array<{ @@ -1347,6 +1345,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { await agentPolicyService.bumpRevision(soClient, esClient, agentPolicyId, { user: options?.user, removeProtection, + asyncDeploy: options?.asyncDeploy, }); }); @@ -2368,6 +2367,7 @@ class PackagePolicyClientWithAuthz extends PackagePolicyClientImpl { user?: AuthenticatedUser | undefined; bumpRevision?: boolean | undefined; force?: true | undefined; + asyncDeploy?: boolean; } | undefined ): Promise<{ diff --git a/x-pack/plugins/fleet/server/services/package_policy_service.ts b/x-pack/plugins/fleet/server/services/package_policy_service.ts index 967efb1055cfb..5a83c2adf97ab 100644 --- a/x-pack/plugins/fleet/server/services/package_policy_service.ts +++ b/x-pack/plugins/fleet/server/services/package_policy_service.ts @@ -105,6 +105,7 @@ export interface PackagePolicyClient { bumpRevision?: boolean; force?: true; authorizationHeader?: HTTPAuthorizationHeader | null; + asyncDeploy?: boolean; } ): Promise<{ created: PackagePolicy[]; @@ -115,7 +116,7 @@ export interface PackagePolicyClient { soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, packagePolicyUpdates: UpdatePackagePolicy[], - options?: { user?: AuthenticatedUser; force?: boolean }, + options?: { user?: AuthenticatedUser; force?: boolean; asyncDeploy?: boolean }, currentVersion?: string ): Promise<{ updatedPolicies: PackagePolicy[] | null; @@ -165,6 +166,7 @@ export interface PackagePolicyClient { user?: AuthenticatedUser; skipUnassignFromAgentPolicies?: boolean; force?: boolean; + asyncDeploy?: boolean; }, context?: RequestHandlerContext, request?: KibanaRequest diff --git a/x-pack/plugins/fleet/server/services/spaces/agent_policy.ts b/x-pack/plugins/fleet/server/services/spaces/agent_policy.ts index 14d7f45f2c47c..2f8d5ff1b14c7 100644 --- a/x-pack/plugins/fleet/server/services/spaces/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/spaces/agent_policy.ts @@ -115,12 +115,30 @@ export async function updateAgentPolicySpaces({ // Update fleet server index agents, enrollment api keys await esClient.updateByQuery({ index: ENROLLMENT_API_KEYS_INDEX, + query: { + bool: { + must: { + terms: { + policy_id: [agentPolicyId], + }, + }, + }, + }, script: `ctx._source.namespaces = [${newSpaceIds.map((spaceId) => `"${spaceId}"`).join(',')}]`, ignore_unavailable: true, refresh: true, }); await esClient.updateByQuery({ index: AGENTS_INDEX, + query: { + bool: { + must: { + terms: { + policy_id: [agentPolicyId], + }, + }, + }, + }, script: `ctx._source.namespaces = [${newSpaceIds.map((spaceId) => `"${spaceId}"`).join(',')}]`, ignore_unavailable: true, refresh: true, diff --git a/x-pack/plugins/fleet/server/types/rest_spec/agent.ts b/x-pack/plugins/fleet/server/types/rest_spec/agent.ts index c1dface818f28..f9db02b92a659 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/agent.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/agent.ts @@ -217,11 +217,6 @@ export const AgentResponseSchema = schema.object({ }); export const GetAgentsResponseSchema = ListResponseSchema(AgentResponseSchema).extends({ - list: schema.maybe( - schema.arrayOf(AgentResponseSchema, { - meta: { deprecated: true }, - }) - ), statusSummary: schema.maybe(schema.recordOf(AgentStatusSchema, schema.number())), }); @@ -377,15 +372,6 @@ export const PostBulkAgentUpgradeRequestSchema = { }), }; -export const PutAgentReassignRequestSchemaDeprecated = { - params: schema.object({ - agentId: schema.string(), - }), - body: schema.object({ - policy_id: schema.string(), - }), -}; - export const PostAgentReassignRequestSchema = { params: schema.object({ agentId: schema.string(), @@ -518,9 +504,6 @@ export const GetAgentStatusRequestSchema = { return validationObj?.error; } }, - meta: { - deprecated: true, - }, }) ), }), @@ -529,11 +512,6 @@ export const GetAgentStatusRequestSchema = { export const GetAgentStatusResponseSchema = schema.object({ results: schema.object({ events: schema.number(), - total: schema.number({ - meta: { - deprecated: true, - }, - }), online: schema.number(), error: schema.number(), offline: schema.number(), diff --git a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts index bd119a77378af..608d2ce5390da 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts @@ -298,6 +298,7 @@ export const createDataStreamPayload = (dataStream: Partial): DataSt enabled: true, data_retention: '7d', }, + indexMode: 'standard', ...dataStream, }); diff --git a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts index a4ea7b9296e28..1d7ee65790cfd 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts @@ -205,8 +205,8 @@ describe('Data Streams tab', () => { const { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', 'dataStream1', 'green', '1', '7 days', 'Delete'], - ['', 'dataStream2', 'green', '1', '5 days ', 'Delete'], + ['', 'dataStream1', 'green', '1', 'Standard', '7 days', 'Delete'], + ['', 'dataStream2', 'green', '1', 'Standard', '5 days ', 'Delete'], ]); }); @@ -254,6 +254,7 @@ describe('Data Streams tab', () => { 'December 31st, 1969 7:00:00 PM', '5b', '1', + 'Standard', '7 days', 'Delete', ], @@ -264,6 +265,7 @@ describe('Data Streams tab', () => { 'December 31st, 1969 7:00:00 PM', '1kb', '1', + 'Standard', '5 days ', 'Delete', ], @@ -289,6 +291,7 @@ describe('Data Streams tab', () => { 'December 31st, 1969 7:00:00 PM', '5b', '1', + 'Standard', '7 days', 'Delete', ], @@ -299,6 +302,7 @@ describe('Data Streams tab', () => { 'December 31st, 1969 7:00:00 PM', '1kb', '1', + 'Standard', '5 days ', 'Delete', ], @@ -346,8 +350,8 @@ describe('Data Streams tab', () => { const { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', 'dataStream1', 'green', '156kb', '10000', '1', '7 days', 'Delete'], - ['', 'dataStream2', 'green', '156kb', '10000', '1', '5 days ', 'Delete'], + ['', 'dataStream1', 'green', '156kb', '10000', '1', 'Standard', '7 days', 'Delete'], + ['', 'dataStream2', 'green', '156kb', '10000', '1', 'Standard', '5 days ', 'Delete'], ]); }); @@ -378,6 +382,7 @@ describe('Data Streams tab', () => { 'December 31st, 1969 7:00:00 PM', '5b', '1', + 'Standard', '7 days', 'Delete', ], @@ -388,6 +393,7 @@ describe('Data Streams tab', () => { 'December 31st, 1969 7:00:00 PM', '1kb', '1', + 'Standard', '5 days ', 'Delete', ], @@ -509,8 +515,8 @@ describe('Data Streams tab', () => { const { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', 'dataStream1', 'green', '1', 'Disabled', 'Delete'], - ['', 'dataStream2', 'green', '1', '', 'Delete'], + ['', 'dataStream1', 'green', '1', 'Standard', 'Disabled', 'Delete'], + ['', 'dataStream2', 'green', '1', 'Standard', '', 'Delete'], ]); await actions.clickNameAt(0); @@ -892,8 +898,16 @@ describe('Data Streams tab', () => { const { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', `managed-data-stream${nonBreakingSpace}Managed`, 'green', '1', '7 days', 'Delete'], - ['', 'non-managed-data-stream', 'green', '1', '7 days', 'Delete'], + [ + '', + `managed-data-stream${nonBreakingSpace}Managed`, + 'green', + '1', + 'Standard', + '7 days', + 'Delete', + ], + ['', 'non-managed-data-stream', 'green', '1', 'Standard', '7 days', 'Delete'], ]); }); @@ -902,15 +916,23 @@ describe('Data Streams tab', () => { let { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', `managed-data-stream${nonBreakingSpace}Managed`, 'green', '1', '7 days', 'Delete'], - ['', 'non-managed-data-stream', 'green', '1', '7 days', 'Delete'], + [ + '', + `managed-data-stream${nonBreakingSpace}Managed`, + 'green', + '1', + 'Standard', + '7 days', + 'Delete', + ], + ['', 'non-managed-data-stream', 'green', '1', 'Standard', '7 days', 'Delete'], ]); actions.toggleViewFilterAt(0); ({ tableCellsValues } = table.getMetaData('dataStreamTable')); expect(tableCellsValues).toEqual([ - ['', 'non-managed-data-stream', 'green', '1', '7 days', 'Delete'], + ['', 'non-managed-data-stream', 'green', '1', 'Standard', '7 days', 'Delete'], ]); }); }); @@ -942,7 +964,15 @@ describe('Data Streams tab', () => { const { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', `hidden-data-stream${nonBreakingSpace}Hidden`, 'green', '1', '7 days', 'Delete'], + [ + '', + `hidden-data-stream${nonBreakingSpace}Hidden`, + 'green', + '1', + 'Standard', + '7 days', + 'Delete', + ], ]); }); }); @@ -989,10 +1019,10 @@ describe('Data Streams tab', () => { const { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', 'dataStreamNoDelete', 'green', '1', '7 days', ''], - ['', 'dataStreamNoEditRetention', 'green', '1', '7 days', 'Delete'], - ['', 'dataStreamNoPermissions', 'green', '1', '7 days', ''], - ['', 'dataStreamWithDelete', 'green', '1', '7 days', 'Delete'], + ['', 'dataStreamNoDelete', 'green', '1', 'Standard', '7 days', ''], + ['', 'dataStreamNoEditRetention', 'green', '1', 'Standard', '7 days', 'Delete'], + ['', 'dataStreamNoPermissions', 'green', '1', 'Standard', '7 days', ''], + ['', 'dataStreamWithDelete', 'green', '1', 'Standard', '7 days', 'Delete'], ]); }); diff --git a/x-pack/plugins/index_management/common/lib/template_serialization.ts b/x-pack/plugins/index_management/common/lib/template_serialization.ts index f8b4ed47a22f7..0ed52e3f04ba0 100644 --- a/x-pack/plugins/index_management/common/lib/template_serialization.ts +++ b/x-pack/plugins/index_management/common/lib/template_serialization.ts @@ -73,6 +73,8 @@ export function deserializeTemplate( type = 'managed'; } + const ilmPolicyName = settings?.index?.lifecycle?.name; + const deserializedTemplate: TemplateDeserialized = { name, version, @@ -80,7 +82,7 @@ export function deserializeTemplate( ...(template.lifecycle ? { lifecycle: deserializeESLifecycle(template.lifecycle) } : {}), indexPatterns: indexPatterns.sort(), template, - ilmPolicy: settings?.index?.lifecycle, + ilmPolicy: ilmPolicyName ? { name: ilmPolicyName } : undefined, composedOf: composedOf ?? [], ignoreMissingComponentTemplates: ignoreMissingComponentTemplates ?? [], dataStream, diff --git a/x-pack/plugins/index_management/common/types/data_streams.ts b/x-pack/plugins/index_management/common/types/data_streams.ts index 78c671969f579..993d32f32bee1 100644 --- a/x-pack/plugins/index_management/common/types/data_streams.ts +++ b/x-pack/plugins/index_management/common/types/data_streams.ts @@ -33,6 +33,8 @@ export type DataStreamIndexFromEs = IndicesDataStreamIndex; export type Health = 'green' | 'yellow' | 'red'; +export type IndexMode = 'standard' | 'logsdb' | 'time_series'; + export interface EnhancedDataStreamFromEs extends IndicesDataStream { global_max_retention?: string; store_size?: IndicesDataStreamsStatsDataStreamsStatsItem['store_size']; @@ -45,6 +47,7 @@ export interface EnhancedDataStreamFromEs extends IndicesDataStream { delete_index: boolean; manage_data_stream_lifecycle: boolean; }; + index_mode?: string | null; } export interface DataStream { @@ -71,6 +74,7 @@ export interface DataStream { retention_determined_by?: string; globalMaxRetention?: string; }; + indexMode: IndexMode; } export interface DataStreamIndex { diff --git a/x-pack/plugins/index_management/common/types/indices.ts b/x-pack/plugins/index_management/common/types/indices.ts index 612aaf3bd6c9b..804a1bce1e299 100644 --- a/x-pack/plugins/index_management/common/types/indices.ts +++ b/x-pack/plugins/index_management/common/types/indices.ts @@ -5,29 +5,9 @@ * 2.0. */ -export type { Index } from '@kbn/index-management-shared-types'; +import { IndicesIndexSettingsKeys } from '@elastic/elasticsearch/lib/api/types'; -export interface IndexModule { - number_of_shards: number | string; - codec: string; - routing_partition_size: number; - refresh_interval: string; - load_fixed_bitset_filters_eagerly: boolean; - shard: { - check_on_startup: boolean | 'checksum'; - }; - number_of_replicas: number; - auto_expand_replicas: false | string; - lifecycle: LifecycleModule; - routing: { - allocation: { - enable: 'all' | 'primaries' | 'new_primaries' | 'none'; - }; - rebalance: { - enable: 'all' | 'primaries' | 'replicas' | 'none'; - }; - }; -} +export type { Index } from '@kbn/index-management-shared-types'; interface AnalysisModule { analyzer: { @@ -41,15 +21,8 @@ interface AnalysisModule { }; } -interface LifecycleModule { - name: string; - rollover_alias?: string; - parse_origination_date?: boolean; - origination_date?: number; -} - export interface IndexSettings { - index?: Partial; + index?: IndicesIndexSettingsKeys; analysis?: AnalysisModule; [key: string]: any; } diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts index 728ba79eedd81..71231c89b673c 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts @@ -623,7 +623,7 @@ export const getFieldsMatchingFilterFromState = ( } => { return Object.fromEntries( Object.entries(state.fields.byId).filter(([_, fieldId]) => - filteredDataTypes.includes(TYPE_DEFINITION[state.fields.byId[fieldId.id].source.type].label) + filteredDataTypes.includes(getTypeLabelFromField(state.fields.byId[fieldId.id].source)) ) ); }; @@ -646,9 +646,7 @@ export const getFieldsFromState = ( const getField = (fieldId: string) => { if (filteredDataTypes) { if ( - filteredDataTypes.includes( - TYPE_DEFINITION[normalizedFields.byId[fieldId].source.type].label - ) + filteredDataTypes.includes(getTypeLabelFromField(normalizedFields.byId[fieldId].source)) ) { return normalizedFields.byId[fieldId]; } diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/use_state_listener.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/use_state_listener.tsx index 5c5e1c6a289fa..26610773ddbf4 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/use_state_listener.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/use_state_listener.tsx @@ -26,9 +26,9 @@ import { deNormalizeRuntimeFields, getAllFieldTypesFromState, getFieldsFromState, + getTypeLabelFromField, } from './lib'; import { useMappingsState, useDispatch } from './mappings_state_context'; -import { TYPE_DEFINITION } from './constants'; interface Args { onChange?: OnUpdateHandler; @@ -56,7 +56,7 @@ export const useMappingsStateListener = ({ onChange, value, status }: Args) => { const allFieldsTypes = getAllFieldTypesFromState(deNormalize(normalize(mappedFields))); return allFieldsTypes.map((dataType) => ({ checked: undefined, - label: TYPE_DEFINITION[dataType].label, + label: getTypeLabelFromField({ type: dataType }), 'data-test-subj': `indexDetailsMappingsSelectFilter-${dataType}`, })); }, [mappedFields]); diff --git a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx index 24544db04498b..9cb5c481b6b50 100644 --- a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx @@ -22,6 +22,7 @@ import { EuiCodeBlock, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { getIndexModeLabel } from '../../../lib/index_mode_labels'; import { allowAutoCreateRadioIds } from '../../../../../common/constants'; import { serializers } from '../../../../shared_imports'; @@ -268,6 +269,19 @@ export const StepReview: React.FunctionComponent = React.memo( {getDescriptionText(serializedSettings)} + {/* Index mode */} + + + + + {getIndexModeLabel( + serializedSettings?.['index.mode'] ?? serializedSettings?.index?.mode + )} + + {/* Mappings */} { describe('getLifecycleValue', () => { @@ -45,4 +45,36 @@ describe('Data stream helpers', () => { ).toBe('5 days'); }); }); + + describe('deserializeGlobalMaxRetention', () => { + it('if globalMaxRetention is undefined', () => { + expect(deserializeGlobalMaxRetention(undefined)).toEqual({}); + }); + + it('split globalMaxRetention size and units', () => { + expect(deserializeGlobalMaxRetention('1000h')).toEqual({ + size: '1000', + unit: 'h', + unitText: 'hours', + }); + }); + + it('support all of the units that are accepted by es', () => { + expect(deserializeGlobalMaxRetention('1000ms')).toEqual({ + size: '1000', + unit: 'ms', + unitText: 'milliseconds', + }); + expect(deserializeGlobalMaxRetention('1000micros')).toEqual({ + size: '1000', + unit: 'micros', + unitText: 'microseconds', + }); + expect(deserializeGlobalMaxRetention('1000nanos')).toEqual({ + size: '1000', + unit: 'nanos', + unitText: 'nanoseconds', + }); + }); + }); }); diff --git a/x-pack/plugins/index_management/public/application/lib/data_streams.tsx b/x-pack/plugins/index_management/public/application/lib/data_streams.tsx index 71c8bb0f61177..6747e84df751d 100644 --- a/x-pack/plugins/index_management/public/application/lib/data_streams.tsx +++ b/x-pack/plugins/index_management/public/application/lib/data_streams.tsx @@ -121,3 +121,19 @@ export const isDSLWithILMIndices = (dataStream?: DataStream | null) => { return; }; + +export const deserializeGlobalMaxRetention = (globalMaxRetention?: string) => { + if (!globalMaxRetention) { + return {}; + } + + const { size, unit } = splitSizeAndUnits(globalMaxRetention); + const availableTimeUnits = [...timeUnits, ...extraTimeUnits]; + const match = availableTimeUnits.find((timeUnit) => timeUnit.value === unit); + + return { + size, + unit, + unitText: match?.text ?? unit, + }; +}; diff --git a/x-pack/plugins/index_management/public/application/lib/index_mode_labels.ts b/x-pack/plugins/index_management/public/application/lib/index_mode_labels.ts new file mode 100644 index 0000000000000..409659b8133c3 --- /dev/null +++ b/x-pack/plugins/index_management/public/application/lib/index_mode_labels.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 { i18n } from '@kbn/i18n'; + +export const getIndexModeLabel = (mode?: string | null) => { + switch (mode) { + case 'standard': + case null: + case undefined: + return i18n.translate('xpack.idxMgmt.indexModeLabels.standardModeLabel', { + defaultMessage: 'Standard', + }); + case 'logsdb': + return i18n.translate('xpack.idxMgmt.indexModeLabels.logsdbModeLabel', { + defaultMessage: 'LogsDB', + }); + case 'time_series': + return i18n.translate('xpack.idxMgmt.indexModeLabels.tsdbModeLabel', { + defaultMessage: 'Time series', + }); + default: + return mode; + } +}; diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx index 974ba6f082042..5b3bf0920c3b7 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx @@ -34,6 +34,7 @@ import { EuiSpacer, } from '@elastic/eui'; +import { getIndexModeLabel } from '../../../../lib/index_mode_labels'; import { DiscoverLink } from '../../../../lib/discover_link'; import { getLifecycleValue } from '../../../../lib/data_streams'; import { SectionLoading, reactRouterNavigate } from '../../../../../shared_imports'; @@ -166,6 +167,7 @@ export const DataStreamDetailPanel: React.FunctionComponent = ({ meteringStorageSize, meteringDocsCount, lifecycle, + indexMode, } = dataStream; const getManagementDetails = () => { @@ -345,6 +347,17 @@ export const DataStreamDetailPanel: React.FunctionComponent = ({ ), dataTestSubj: 'indexTemplateDetail', }, + { + name: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.indexModeTitle', { + defaultMessage: 'Index mode', + }), + toolTip: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.indexModeToolTip', { + defaultMessage: + "The index mode applied to the data stream's backing indices, as defined in its associated index template.", + }), + content: getIndexModeLabel(indexMode), + dataTestSubj: 'indexModeDetail', + }, { name: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.dataRetentionTitle', { defaultMessage: 'Effective data retention', diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx index 927907757fe7b..e91fd644f795c 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx @@ -36,6 +36,7 @@ import { humanizeTimeStamp } from '../humanize_time_stamp'; import { DataStreamsBadges } from '../data_stream_badges'; import { ConditionalWrap } from '../data_stream_detail_panel'; import { isDataStreamFullyManagedByILM } from '../../../../lib/data_streams'; +import { getIndexModeLabel } from '../../../../lib/index_mode_labels'; import { FilterListButton, Filters } from '../../components'; import { type DataStreamFilterName } from '../data_stream_list'; @@ -184,6 +185,16 @@ export const DataStreamTable: React.FunctionComponent = ({ ), }); + columns.push({ + field: 'indexMode', + name: i18n.translate('xpack.idxMgmt.dataStreamList.table.indexModeColumnTitle', { + defaultMessage: 'Index mode', + }), + truncateText: true, + sortable: true, + render: (indexMode: DataStream['indexMode']) => getIndexModeLabel(indexMode), + }); + columns.push({ field: 'lifecycle', name: ( diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/edit_data_retention_modal.tsx b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/edit_data_retention_modal.tsx index f5eee4671481a..2c60e04be31a6 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/edit_data_retention_modal.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/edit_data_retention_modal.tsx @@ -43,7 +43,7 @@ import { getIndexListUri } from '../../../../services/routing'; import { documentationService } from '../../../../services/documentation'; import { splitSizeAndUnits, DataStream } from '../../../../../../common'; import { timeUnits } from '../../../../constants/time_units'; -import { isDSLWithILMIndices } from '../../../../lib/data_streams'; +import { deserializeGlobalMaxRetention, isDSLWithILMIndices } from '../../../../lib/data_streams'; import { useAppContext } from '../../../../app_context'; import { UnitField } from '../../../../components/shared'; import { updateDataRetention } from '../../../../services/api'; @@ -214,6 +214,7 @@ export const EditDataRetentionModal: React.FunctionComponent = ({ const { history } = useAppContext(); const dslWithIlmIndices = isDSLWithILMIndices(dataStream); const { size, unit } = splitSizeAndUnits(lifecycle?.data_retention as string); + const globalMaxRetention = deserializeGlobalMaxRetention(lifecycle?.globalMaxRetention); const { services: { notificationService }, config: { enableTogglingDataRetention, enableProjectLevelRetentionChecks }, @@ -331,8 +332,11 @@ export const EditDataRetentionModal: React.FunctionComponent = ({ <> diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/validations.test.ts b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/validations.test.ts index 0768d0990cdc0..87cc1d36526fb 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/validations.test.ts +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/validations.test.ts @@ -47,6 +47,26 @@ describe('isBiggerThanGlobalMaxRetention', () => { }); }); + it('should correctly compare retention in all of the units that are accepted by es', () => { + // 1000 milliseconds = 1 seconds + expect(isBiggerThanGlobalMaxRetention(1, 's', '1000ms')).toBeUndefined(); + expect(isBiggerThanGlobalMaxRetention(2, 's', '1000ms')).toEqual({ + message: 'Maximum data retention period on this project is 1000 milliseconds.', + }); + + // 1000000 microseconds = 1 seconds + expect(isBiggerThanGlobalMaxRetention(1, 's', '1000000micros')).toBeUndefined(); + expect(isBiggerThanGlobalMaxRetention(2, 'm', '1000000micros')).toEqual({ + message: 'Maximum data retention period on this project is 1000000 microseconds.', + }); + + // 1000000000 microseconds = 1 seconds + expect(isBiggerThanGlobalMaxRetention(2, 's', '1000000000nanos')); + expect(isBiggerThanGlobalMaxRetention(2, 'h', '1000000000nanos')).toEqual({ + message: 'Maximum data retention period on this project is 1000000000 nanoseconds.', + }); + }); + it('should throw an error for unknown time units', () => { expect(() => isBiggerThanGlobalMaxRetention(10, 'x', '30d')).toThrow('Unknown unit: x'); }); diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/validations.ts b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/validations.ts index 831ac2f4c26b9..8486f01fb5b44 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/validations.ts +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/validations.ts @@ -7,34 +7,44 @@ import { i18n } from '@kbn/i18n'; import { splitSizeAndUnits } from '../../../../../../common'; +import { deserializeGlobalMaxRetention } from '../../../../lib/data_streams'; -const convertToMinutes = (value: string) => { +const convertToSeconds = (value: string) => { const { size, unit } = splitSizeAndUnits(value); const sizeNum = parseInt(size, 10); switch (unit) { case 'd': - // days to minutes - return sizeNum * 24 * 60; + // days to seconds + return sizeNum * 24 * 60 * 60; case 'h': - // hours to minutes - return sizeNum * 60; + // hours to seconds + return sizeNum * 60 * 60; case 'm': - // minutes to minutes - return sizeNum; + // minutes to seconds + return sizeNum * 60; case 's': - // seconds to minutes (round up if any remainder) - return Math.ceil(sizeNum / 60); + // seconds to seconds + return sizeNum; + case 'ms': + // milliseconds to seconds + return sizeNum / 1000; + case 'micros': + // microseconds to seconds + return sizeNum / 1000 / 1000; + case 'nanos': + // nanoseconds to seconds + return sizeNum / 1000 / 1000 / 1000; default: throw new Error(`Unknown unit: ${unit}`); } }; const isRetentionBiggerThan = (valueA: string, valueB: string) => { - const minutesA = convertToMinutes(valueA); - const minutesB = convertToMinutes(valueB); + const secondsA = convertToSeconds(valueA); + const secondsB = convertToSeconds(valueB); - return minutesA > minutesB; + return secondsA > secondsB; }; export const isBiggerThanGlobalMaxRetention = ( @@ -46,14 +56,19 @@ export const isBiggerThanGlobalMaxRetention = ( return undefined; } + const { size, unitText } = deserializeGlobalMaxRetention(globalMaxRetention); return isRetentionBiggerThan(`${retentionValue}${retentionTimeUnit}`, globalMaxRetention) ? { message: i18n.translate( 'xpack.idxMgmt.dataStreamsDetailsPanel.editDataRetentionModal.dataRetentionFieldMaxError', { - defaultMessage: 'Maximum data retention period on this project is {maxRetention} days.', + defaultMessage: + 'Maximum data retention period on this project is {maxRetention} {unitText}.', // Remove the unit from the globalMaxRetention value - values: { maxRetention: globalMaxRetention.slice(0, -1) }, + values: { + maxRetention: size, + unitText, + }, } ), } diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table_pagination.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table_pagination.tsx index b9dd98e21a426..a0988aec797f6 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table_pagination.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table_pagination.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { EuiTablePagination } from '@elastic/eui'; import { useEuiTablePersist } from '@kbn/shared-ux-table-persist'; -import { IndexModule } from '../../../../../../common'; +import { Index } from '../../../../../../common'; interface IndexTablePaginationProps { pager: any; @@ -27,7 +27,7 @@ export const IndexTablePagination = ({ readURLParams, setURLParam, }: IndexTablePaginationProps) => { - const { pageSize, onTableChange } = useEuiTablePersist({ + const { pageSize, onTableChange } = useEuiTablePersist({ tableId: 'indices', initialPageSize: pager.itemsPerPage, pageSizeOptions: PAGE_SIZE_OPTIONS, diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx index 513377714ffe0..ff06a08014f61 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx @@ -28,6 +28,7 @@ import { TemplateDeserialized } from '../../../../../../../common'; import { ILM_PAGES_POLICY_EDIT } from '../../../../../constants'; import { useIlmLocator } from '../../../../../services/use_ilm_locator'; import { allowAutoCreateRadioIds } from '../../../../../../../common/constants'; +import { getIndexModeLabel } from '../../../../../lib/index_mode_labels'; interface Props { templateDetails: TemplateDeserialized; @@ -57,6 +58,7 @@ export const TabSummary: React.FunctionComponent = ({ templateDetails }) _meta, _kbnMeta: { isLegacy, hasDatastream }, allowAutoCreate, + template, } = templateDetails; const numIndexPatterns = indexPatterns.length; @@ -221,6 +223,17 @@ export const TabSummary: React.FunctionComponent = ({ templateDetails }) )} + {/* Index mode */} + + + + + {getIndexModeLabel(template?.settings?.index?.mode)} + + {/* Allow auto create */} {isLegacy !== true && allowAutoCreate !== allowAutoCreateRadioIds.NO_OVERWRITE_RADIO_OPTION && ( diff --git a/x-pack/plugins/index_management/server/lib/data_stream_serialization.ts b/x-pack/plugins/index_management/server/lib/data_stream_serialization.ts index 2e493ca02aa79..31c0baa6c6b8c 100644 --- a/x-pack/plugins/index_management/server/lib/data_stream_serialization.ts +++ b/x-pack/plugins/index_management/server/lib/data_stream_serialization.ts @@ -6,6 +6,7 @@ */ import { ByteSizeValue } from '@kbn/config-schema'; +import { IndexMode } from '../../common/types/data_streams'; import type { DataStream, EnhancedDataStreamFromEs, Health } from '../../common'; export function deserializeDataStream(dataStreamFromEs: EnhancedDataStreamFromEs): DataStream { @@ -28,6 +29,7 @@ export function deserializeDataStream(dataStreamFromEs: EnhancedDataStreamFromEs lifecycle, global_max_retention: globalMaxRetention, next_generation_managed_by: nextGenerationManagedBy, + index_mode: indexMode, } = dataStreamFromEs; const meteringStorageSize = meteringStorageSizeBytes !== undefined @@ -73,6 +75,7 @@ export function deserializeDataStream(dataStreamFromEs: EnhancedDataStreamFromEs globalMaxRetention, }, nextGenerationManagedBy, + indexMode: (indexMode ?? 'standard') as IndexMode, }; } diff --git a/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts b/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts index 8b62c2b3a25cb..cd47b8cc9e0bb 100644 --- a/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts @@ -11,6 +11,7 @@ import { IScopedClusterClient } from '@kbn/core/server'; import { IndicesDataStream, IndicesDataStreamsStatsDataStreamsStatsItem, + IndicesGetIndexTemplateIndexTemplateItem, SecurityHasPrivilegesResponse, } from '@elastic/elasticsearch/lib/api/types'; import type { MeteringStats } from '../../../lib/types'; @@ -31,12 +32,14 @@ const enhanceDataStreams = ({ meteringStats, dataStreamsPrivileges, globalMaxRetention, + indexTemplates, }: { dataStreams: IndicesDataStream[]; dataStreamsStats?: IndicesDataStreamsStatsDataStreamsStatsItem[]; meteringStats?: MeteringStats[]; dataStreamsPrivileges?: SecurityHasPrivilegesResponse; globalMaxRetention?: string; + indexTemplates?: IndicesGetIndexTemplateIndexTemplateItem[]; }): EnhancedDataStreamFromEs[] => { return dataStreams.map((dataStream) => { const enhancedDataStream: EnhancedDataStreamFromEs = { @@ -71,6 +74,16 @@ const enhanceDataStreams = ({ } } + if (indexTemplates) { + const indexTemplate = indexTemplates.find( + (template) => template.name === dataStream.template + ); + if (indexTemplate) { + enhancedDataStream.index_mode = + indexTemplate.index_template?.template?.settings?.index?.mode; + } + } + return enhancedDataStream; }); }; @@ -152,11 +165,15 @@ export function registerGetAllRoute({ router, lib: { handleEsError }, config }: ); } + const { index_templates: indexTemplates } = + await client.asCurrentUser.indices.getIndexTemplate(); + const enhancedDataStreams = enhanceDataStreams({ dataStreams, dataStreamsStats, meteringStats, dataStreamsPrivileges, + indexTemplates, }); return response.ok({ body: deserializeDataStreamList(enhancedDataStreams) }); @@ -199,17 +216,30 @@ export function registerGetOneRoute({ router, lib: { handleEsError }, config }: if (dataStreams[0]) { let dataStreamsPrivileges; + let indexTemplates; if (config.isSecurityEnabled()) { dataStreamsPrivileges = await getDataStreamsPrivileges(client, [dataStreams[0].name]); } + if (dataStreams[0].template) { + const { index_templates: templates } = + await client.asCurrentUser.indices.getIndexTemplate({ + name: dataStreams[0].template, + }); + + if (templates) { + indexTemplates = templates; + } + } + const enhancedDataStreams = enhanceDataStreams({ dataStreams, dataStreamsStats, meteringStats, dataStreamsPrivileges, globalMaxRetention, + indexTemplates, }); const body = deserializeDataStream(enhancedDataStreams[0]); return response.ok({ body }); diff --git a/x-pack/plugins/inference/README.md b/x-pack/plugins/inference/README.md index 1807da7f29faa..935ae31bd6bc6 100644 --- a/x-pack/plugins/inference/README.md +++ b/x-pack/plugins/inference/README.md @@ -4,13 +4,12 @@ The inference plugin is a central place to handle all interactions with the Elas external LLM APIs. Its goals are: - Provide a single place for all interactions with large language models and other generative AI adjacent tasks. -- Abstract away differences between different LLM providers like OpenAI, Bedrock and Gemini -- Host commonly used LLM-based tasks like generating ES|QL from natural language and knowledge base recall. +- Abstract away differences between different LLM providers like OpenAI, Bedrock and Gemini. - Allow us to move gradually to the \_inference endpoint without disrupting engineers. ## Architecture and examples -![CleanShot 2024-07-14 at 14 45 27@2x](https://github.com/user-attachments/assets/e65a3e47-bce1-4dcf-bbed-4f8ac12a104f) +![architecture-schema](https://github.com/user-attachments/assets/e65a3e47-bce1-4dcf-bbed-4f8ac12a104f) ## Terminology @@ -21,8 +20,22 @@ The following concepts are commonly used throughout the plugin: - **tools**: a set of tools that the LLM can choose to use when generating the next message. In essence, it allows the consumer of the API to define a schema for structured output instead of plain text, and having the LLM select the most appropriate one. - **tool call**: when the LLM has chosen a tool (schema) to use for its output, and returns a document that matches the schema, this is referred to as a tool call. +## Inference connectors + +Performing inference, or more globally communicating with the LLM, is done using stack connectors. + +The subset of connectors that can be used for inference are called `genAI`, or `inference` connectors. +Calling any inference APIs with the ID of a connector that is not inference-compatible will result in the API throwing an error. + +The list of inference connector types: +- `.gen-ai`: OpenAI connector +- `.bedrock`: Bedrock Claude connector +- `.gemini`: Vertex Gemini connector + ## Usage examples +The inference APIs are available via the inference client, which can be created using the inference plugin's start contract: + ```ts class MyPlugin { setup(coreSetup, pluginsSetup) { @@ -40,9 +53,9 @@ class MyPlugin { async (context, request, response) => { const [coreStart, pluginsStart] = await coreSetup.getStartServices(); - const inferenceClient = pluginsSetup.inference.getClient({ request }); + const inferenceClient = pluginsStart.inference.getClient({ request }); - const chatComplete$ = inferenceClient.chatComplete({ + const chatResponse = inferenceClient.chatComplete({ connectorId: request.body.connectorId, system: `Here is my system message`, messages: [ @@ -53,13 +66,9 @@ class MyPlugin { ], }); - const message = await lastValueFrom( - chatComplete$.pipe(withoutTokenCountEvents(), withoutChunkEvents()) - ); - return response.ok({ body: { - message, + chatResponse, }, }); } @@ -68,33 +77,190 @@ class MyPlugin { } ``` -## Services +## APIs -### `chatComplete`: +### `chatComplete` API: `chatComplete` generates a response to a prompt or a conversation using the LLM. Here's what is supported: -- Normalizing request and response formats from different connector types (e.g. OpenAI, Bedrock, Claude, Elastic Inference Service) +- Normalizing request and response formats from all supported connector types - Tool calling and validation of tool calls -- Emits token count events -- Emits message events, which is the concatenated message based on the response chunks +- Token usage stats / events +- Streaming mode to work with chunks in real time instead of waiting for the full response + +#### Standard usage + +In standard mode, the API returns a promise resolving with the full LLM response once the generation is complete. +The response will also contain the token count info, if available. + +```ts +const chatResponse = inferenceClient.chatComplete({ + connectorId: 'some-gen-ai-connector', + system: `Here is my system message`, + messages: [ + { + role: MessageRole.User, + content: 'Do something', + }, + ], +}); + +const { content, tokens } = chatResponse; +// do something with the output +``` + +#### Streaming mode + +Passing `stream: true` when calling the API enables streaming mode. +In that mode, the API returns an observable instead of a promise, emitting chunks in real time. + +That observable emits three types of events: + +- `chunk` the completion chunks, emitted in real time +- `tokenCount` token count event, containing info about token usages, eventually emitted after the chunks +- `message` full message event, emitted once the source is done sending chunks + +The `@kbn/inference-common` package exposes various utilities to work with this multi-events observable: + +- `isChatCompletionChunkEvent`, `isChatCompletionMessageEvent` and `isChatCompletionTokenCountEvent` which are type guard for the corresponding event types +- `withoutChunkEvents` and `withoutTokenCountEvents` + +```ts +import { + isChatCompletionChunkEvent, + isChatCompletionMessageEvent, + withoutTokenCountEvents, + withoutChunkEvents, +} from '@kbn/inference-common'; -### `output` +const chatComplete$ = inferenceClient.chatComplete({ + connectorId: 'some-gen-ai-connector', + stream: true, + system: `Here is my system message`, + messages: [ + { + role: MessageRole.User, + content: 'Do something', + }, + ], +}); -`output` is a wrapper around `chatComplete` that is catered towards a single use case: having the LLM output a structured response, based on a schema. It also drops the token count events to simplify usage. +// using and filtering the events +chatComplete$.pipe(withoutTokenCountEvents()).subscribe((event) => { + if (isChatCompletionChunkEvent(event)) { + // do something with the chunk event + } else { + // do something with the message event + } +}); + +// or retrieving the final message +const message = await lastValueFrom( + chatComplete$.pipe(withoutTokenCountEvents(), withoutChunkEvents()) +); +``` + +#### Defining and using tools + +Tools are defined as a record, with a `description` and optionally a `schema`. The reason why it's a record is because of type-safety. +This allows us to have fully typed tool calls (e.g. when the name of the tool being called is `x`, its arguments are typed as the schema of `x`). + +The description and schema of a tool will be converted and sent to the LLM, so it's important +to be explicit about what each tool does. + +```ts +const chatResponse = inferenceClient.chatComplete({ + connectorId: 'some-gen-ai-connector', + system: `Here is my system message`, + messages: [ + { + role: MessageRole.User, + content: 'How much is 4 plus 9?', + }, + ], + toolChoice: ToolChoiceType.required, // MUST call a tool + tools: { + date: { + description: 'Call this tool if you need to know the current date' + }, + add: { + description: 'This tool can be used to add two numbers', + schema: { + type: 'object', + properties: { + a: { type: 'number', description: 'the first number' }, + b: { type: 'number', description: 'the second number'} + }, + required: ['a', 'b'] + } + } + } as const // as const is required to have type inference on the schema +}); -### Observable event streams +const { content, toolCalls } = chatResponse; +const toolCall = toolCalls[0]; +// process the tool call and eventually continue the conversation with the LLM +``` + +### `output` API + +`output` is a wrapper around the `chatComplete` API that is catered towards a specific use case: having the LLM output a structured response, based on a schema. +It's basically just making sure that the LLM will call the single tool that is exposed via the provided `schema`. +It also drops the token count info to simplify usage. + +Similar to `chatComplete`, `output` supports two modes: normal full response mode by default, and optional streaming mode by passing the `stream: true` parameter. + +```ts +import { ToolSchema } from '@kbn/inference-common'; -These APIs, both on the client and the server, return Observables that emit events. When converting the Observable into a stream, the following things happen: +// schema must be defined as full const or using the `satisfies ToolSchema` modifier for TS type inference to work +const mySchema = { + type: 'object', + properties: { + animals: { + description: 'the list of animals that are mentioned in the provided article', + type: 'array', + items: { + type: 'string', + }, + }, + vegetables: { + description: 'the list of vegetables that are mentioned in the provided article', + type: 'array', + items: { + type: 'string', + }, + }, + }, +} as const; -- Errors are caught and serialized as events sent over the stream (after an error, the stream ends). -- The response stream outputs data as [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) -- The client that reads the stream, parses the event source as an Observable, and if it encounters a serialized error, it deserializes it and throws an error in the Observable. +const response = inferenceClient.outputApi({ + id: 'extract_from_article', + connectorId: 'some-gen-ai-connector', + schema: mySchema, + system: + 'You are a helpful assistant and your current task is to extract informations from the provided document', + input: ` + Please find all the animals and vegetables that are mentioned in the following document: + + ## Document + + ${theDoc} + `, +}); + +// output is properly typed from the provided schema +const { animals, vegetables } = response.output; +``` ### Errors -All known errors are instances, and not extensions, from the `InferenceTaskError` base class, which has a `code`, a `message`, and `meta` information about the error. This allows us to serialize and deserialize errors over the wire without a complicated factory pattern. +All known errors are instances, and not extensions, of the `InferenceTaskError` base class, which has a `code`, a `message`, and `meta` information about the error. +This allows us to serialize and deserialize errors over the wire without a complicated factory pattern. -### Tools +Type guards for each type of error are exposed from the `@kbn/inference-common` package, such as: -Tools are defined as a record, with a `description` and optionally a `schema`. The reason why it's a record is because of type-safety. This allows us to have fully typed tool calls (e.g. when the name of the tool being called is `x`, its arguments are typed as the schema of `x`). +- `isInferenceError` +- `isInferenceInternalError` +- `isInferenceRequestError` +- ...`isXXXError` diff --git a/x-pack/plugins/inference/common/chat_complete/index.ts b/x-pack/plugins/inference/common/chat_complete/index.ts deleted file mode 100644 index aef9de12ba7a9..0000000000000 --- a/x-pack/plugins/inference/common/chat_complete/index.ts +++ /dev/null @@ -1,99 +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 { Observable } from 'rxjs'; -import type { InferenceTaskEventBase } from '../inference_task'; -import type { ToolCall, ToolCallsOf, ToolOptions } from './tools'; - -export enum MessageRole { - User = 'user', - Assistant = 'assistant', - Tool = 'tool', -} - -interface MessageBase { - role: TRole; -} - -export type UserMessage = MessageBase & { content: string }; - -export type AssistantMessage = MessageBase & { - content: string | null; - toolCalls?: Array | undefined>>; -}; - -export type ToolMessage | unknown> = - MessageBase & { - toolCallId: string; - response: TToolResponse; - }; - -export type Message = UserMessage | AssistantMessage | ToolMessage; - -export type ChatCompletionMessageEvent = - InferenceTaskEventBase & { - content: string; - } & { toolCalls: ToolCallsOf['toolCalls'] }; - -export type ChatCompletionResponse = Observable< - ChatCompletionEvent ->; - -export enum ChatCompletionEventType { - ChatCompletionChunk = 'chatCompletionChunk', - ChatCompletionTokenCount = 'chatCompletionTokenCount', - ChatCompletionMessage = 'chatCompletionMessage', -} - -export interface ChatCompletionChunkToolCall { - index: number; - toolCallId: string; - function: { - name: string; - arguments: string; - }; -} - -export type ChatCompletionChunkEvent = - InferenceTaskEventBase & { - content: string; - tool_calls: ChatCompletionChunkToolCall[]; - }; - -export type ChatCompletionTokenCountEvent = - InferenceTaskEventBase & { - tokens: { - prompt: number; - completion: number; - total: number; - }; - }; - -export type ChatCompletionEvent = - | ChatCompletionChunkEvent - | ChatCompletionTokenCountEvent - | ChatCompletionMessageEvent; - -export type FunctionCallingMode = 'native' | 'simulated'; - -/** - * Request a completion from the LLM based on a prompt or conversation. - * - * @param {string} options.connectorId The ID of the connector to use - * @param {string} [options.system] A system message that defines the behavior of the LLM. - * @param {Message[]} options.message A list of messages that make up the conversation to be completed. - * @param {ToolChoice} [options.toolChoice] Force the LLM to call a (specific) tool, or no tool - * @param {Record} [options.tools] A map of tools that can be called by the LLM - */ -export type ChatCompleteAPI = ( - options: { - connectorId: string; - system?: string; - messages: Message[]; - functionCalling?: FunctionCallingMode; - } & TToolOptions -) => ChatCompletionResponse; diff --git a/x-pack/plugins/inference/common/chat_complete/is_chat_completion_chunk_event.ts b/x-pack/plugins/inference/common/chat_complete/is_chat_completion_chunk_event.ts deleted file mode 100644 index 1630d765ab81e..0000000000000 --- a/x-pack/plugins/inference/common/chat_complete/is_chat_completion_chunk_event.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ChatCompletionChunkEvent, ChatCompletionEvent, ChatCompletionEventType } from '.'; - -export function isChatCompletionChunkEvent( - event: ChatCompletionEvent -): event is ChatCompletionChunkEvent { - return event.type === ChatCompletionEventType.ChatCompletionChunk; -} diff --git a/x-pack/plugins/inference/common/chat_complete/is_chat_completion_event.ts b/x-pack/plugins/inference/common/chat_complete/is_chat_completion_event.ts deleted file mode 100644 index d4d9305cac94b..0000000000000 --- a/x-pack/plugins/inference/common/chat_complete/is_chat_completion_event.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ChatCompletionEvent, ChatCompletionEventType } from '.'; -import { InferenceTaskEvent } from '../inference_task'; - -export function isChatCompletionEvent(event: InferenceTaskEvent): event is ChatCompletionEvent { - return ( - event.type === ChatCompletionEventType.ChatCompletionChunk || - event.type === ChatCompletionEventType.ChatCompletionMessage || - event.type === ChatCompletionEventType.ChatCompletionTokenCount - ); -} diff --git a/x-pack/plugins/inference/common/chat_complete/is_chat_completion_message_event.ts b/x-pack/plugins/inference/common/chat_complete/is_chat_completion_message_event.ts deleted file mode 100644 index 172e55df9e4b4..0000000000000 --- a/x-pack/plugins/inference/common/chat_complete/is_chat_completion_message_event.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ChatCompletionEvent, ChatCompletionEventType, ChatCompletionMessageEvent } from '.'; -import type { ToolOptions } from './tools'; - -export function isChatCompletionMessageEvent>( - event: ChatCompletionEvent -): event is ChatCompletionMessageEvent { - return event.type === ChatCompletionEventType.ChatCompletionMessage; -} diff --git a/x-pack/plugins/inference/common/chat_complete/without_chunk_events.ts b/x-pack/plugins/inference/common/chat_complete/without_chunk_events.ts deleted file mode 100644 index 58e72e2c90903..0000000000000 --- a/x-pack/plugins/inference/common/chat_complete/without_chunk_events.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 { filter, OperatorFunction } from 'rxjs'; -import { ChatCompletionChunkEvent, ChatCompletionEvent, ChatCompletionEventType } from '.'; - -export function withoutChunkEvents(): OperatorFunction< - T, - Exclude -> { - return filter( - (event): event is Exclude => - event.type !== ChatCompletionEventType.ChatCompletionChunk - ); -} diff --git a/x-pack/plugins/inference/common/chat_complete/without_token_count_events.ts b/x-pack/plugins/inference/common/chat_complete/without_token_count_events.ts deleted file mode 100644 index 1b7dbdb9c1372..0000000000000 --- a/x-pack/plugins/inference/common/chat_complete/without_token_count_events.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 { filter, OperatorFunction } from 'rxjs'; -import { ChatCompletionEvent, ChatCompletionEventType, ChatCompletionTokenCountEvent } from '.'; - -export function withoutTokenCountEvents(): OperatorFunction< - T, - Exclude -> { - return filter( - (event): event is Exclude => - event.type !== ChatCompletionEventType.ChatCompletionTokenCount - ); -} diff --git a/x-pack/plugins/inference/common/connectors.ts b/x-pack/plugins/inference/common/connectors.ts index f7ad616741d79..ee628f520feff 100644 --- a/x-pack/plugins/inference/common/connectors.ts +++ b/x-pack/plugins/inference/common/connectors.ts @@ -22,7 +22,3 @@ export interface InferenceConnector { export function isSupportedConnectorType(id: string): id is InferenceConnectorType { return allSupportedConnectorTypes.includes(id as InferenceConnectorType); } - -export interface GetConnectorsResponseBody { - connectors: InferenceConnector[]; -} diff --git a/x-pack/plugins/inference/common/create_output_api.test.ts b/x-pack/plugins/inference/common/create_output_api.test.ts new file mode 100644 index 0000000000000..b5d380fa9aac6 --- /dev/null +++ b/x-pack/plugins/inference/common/create_output_api.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { firstValueFrom, isObservable, of, toArray } from 'rxjs'; +import { + ChatCompleteResponse, + ChatCompletionEvent, + ChatCompletionEventType, +} from '@kbn/inference-common'; +import { createOutputApi } from './create_output_api'; + +describe('createOutputApi', () => { + let chatComplete: jest.Mock; + + beforeEach(() => { + chatComplete = jest.fn(); + }); + + it('calls `chatComplete` with the right parameters', async () => { + chatComplete.mockResolvedValue(Promise.resolve({ content: 'content', toolCalls: [] })); + + const output = createOutputApi(chatComplete); + + await output({ + id: 'id', + stream: false, + functionCalling: 'native', + connectorId: '.my-connector', + system: 'system', + input: 'input message', + }); + + expect(chatComplete).toHaveBeenCalledTimes(1); + expect(chatComplete).toHaveBeenCalledWith({ + connectorId: '.my-connector', + functionCalling: 'native', + stream: false, + system: 'system', + messages: [ + { + content: 'input message', + role: 'user', + }, + ], + }); + }); + + it('returns the expected value when stream=false', async () => { + const chatCompleteResponse: ChatCompleteResponse = { + content: 'content', + toolCalls: [{ toolCallId: 'a', function: { name: 'foo', arguments: { arg: 1 } } }], + }; + + chatComplete.mockResolvedValue(Promise.resolve(chatCompleteResponse)); + + const output = createOutputApi(chatComplete); + + const response = await output({ + id: 'my-id', + stream: false, + connectorId: '.my-connector', + input: 'input message', + }); + + expect(response).toEqual({ + id: 'my-id', + content: chatCompleteResponse.content, + output: chatCompleteResponse.toolCalls[0].function.arguments, + }); + }); + + it('returns the expected value when stream=true', async () => { + const sourceEvents: ChatCompletionEvent[] = [ + { type: ChatCompletionEventType.ChatCompletionChunk, content: 'chunk-1', tool_calls: [] }, + { type: ChatCompletionEventType.ChatCompletionChunk, content: 'chunk-2', tool_calls: [] }, + { + type: ChatCompletionEventType.ChatCompletionMessage, + content: 'message', + toolCalls: [{ toolCallId: 'a', function: { name: 'foo', arguments: { arg: 1 } } }], + }, + ]; + + chatComplete.mockReturnValue(of(...sourceEvents)); + + const output = createOutputApi(chatComplete); + + const response$ = await output({ + id: 'my-id', + stream: true, + connectorId: '.my-connector', + input: 'input message', + }); + + expect(isObservable(response$)).toEqual(true); + const events = await firstValueFrom(response$.pipe(toArray())); + + expect(events).toEqual([ + { + content: 'chunk-1', + id: 'my-id', + type: 'output', + }, + { + content: 'chunk-2', + id: 'my-id', + type: 'output', + }, + { + content: 'message', + id: 'my-id', + output: { + arg: 1, + }, + type: 'complete', + }, + ]); + }); +}); diff --git a/x-pack/plugins/inference/common/create_output_api.ts b/x-pack/plugins/inference/common/create_output_api.ts new file mode 100644 index 0000000000000..e5dd2eeda2cbd --- /dev/null +++ b/x-pack/plugins/inference/common/create_output_api.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 { + ChatCompleteAPI, + ChatCompletionEventType, + MessageRole, + OutputAPI, + OutputEventType, + OutputOptions, + ToolSchema, + withoutTokenCountEvents, +} from '@kbn/inference-common'; +import { isObservable, map } from 'rxjs'; +import { ensureMultiTurn } from './utils/ensure_multi_turn'; + +export function createOutputApi(chatCompleteApi: ChatCompleteAPI): OutputAPI; +export function createOutputApi(chatCompleteApi: ChatCompleteAPI) { + return ({ + id, + connectorId, + input, + schema, + system, + previousMessages, + functionCalling, + stream, + }: OutputOptions) => { + const response = chatCompleteApi({ + connectorId, + stream, + functionCalling, + system, + messages: ensureMultiTurn([ + ...(previousMessages || []), + { + role: MessageRole.User, + content: input, + }, + ]), + ...(schema + ? { + tools: { + structuredOutput: { + description: `Use the following schema to respond to the user's request in structured data, so it can be parsed and handled.`, + schema, + }, + }, + toolChoice: { function: 'structuredOutput' as const }, + } + : {}), + }); + + if (isObservable(response)) { + return response.pipe( + withoutTokenCountEvents(), + map((event) => { + if (event.type === ChatCompletionEventType.ChatCompletionChunk) { + return { + type: OutputEventType.OutputUpdate, + id, + content: event.content, + }; + } + + return { + id, + output: + event.toolCalls.length && 'arguments' in event.toolCalls[0].function + ? event.toolCalls[0].function.arguments + : undefined, + content: event.content, + type: OutputEventType.OutputComplete, + }; + }) + ); + } else { + return response.then((chatResponse) => { + return { + id, + content: chatResponse.content, + output: + chatResponse.toolCalls.length && 'arguments' in chatResponse.toolCalls[0].function + ? chatResponse.toolCalls[0].function.arguments + : undefined, + }; + }); + } + }; +} diff --git a/x-pack/plugins/inference/common/chat_complete/request.ts b/x-pack/plugins/inference/common/http_apis.ts similarity index 66% rename from x-pack/plugins/inference/common/chat_complete/request.ts rename to x-pack/plugins/inference/common/http_apis.ts index 1038e481a6260..c07fcd29b2211 100644 --- a/x-pack/plugins/inference/common/chat_complete/request.ts +++ b/x-pack/plugins/inference/common/http_apis.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { Message, FunctionCallingMode } from '.'; -import type { ToolOptions } from './tools'; +import type { FunctionCallingMode, Message, ToolOptions } from '@kbn/inference-common'; +import { InferenceConnector } from './connectors'; export type ChatCompleteRequestBody = { connectorId: string; @@ -15,3 +15,7 @@ export type ChatCompleteRequestBody = { messages: Message[]; functionCalling?: FunctionCallingMode; } & ToolOptions; + +export interface GetConnectorsResponseBody { + connectors: InferenceConnector[]; +} diff --git a/x-pack/plugins/inference/common/index.ts b/x-pack/plugins/inference/common/index.ts index 58c84a47c1804..19b24d53a389a 100644 --- a/x-pack/plugins/inference/common/index.ts +++ b/x-pack/plugins/inference/common/index.ts @@ -10,22 +10,8 @@ export { splitIntoCommands, } from './tasks/nl_to_esql/correct_common_esql_mistakes'; -export { isChatCompletionChunkEvent } from './chat_complete/is_chat_completion_chunk_event'; -export { isChatCompletionMessageEvent } from './chat_complete/is_chat_completion_message_event'; -export { isChatCompletionEvent } from './chat_complete/is_chat_completion_event'; +export { generateFakeToolCallId } from './utils/generate_fake_tool_call_id'; -export { isOutputUpdateEvent } from './output/is_output_update_event'; -export { isOutputCompleteEvent } from './output/is_output_complete_event'; -export { isOutputEvent } from './output/is_output_event'; +export { createOutputApi } from './create_output_api'; -export type { ToolSchema } from './chat_complete/tool_schema'; - -export { - type Message, - MessageRole, - type ToolMessage, - type AssistantMessage, - type UserMessage, -} from './chat_complete'; - -export { generateFakeToolCallId } from './chat_complete/generate_fake_tool_call_id'; +export type { ChatCompleteRequestBody, GetConnectorsResponseBody } from './http_apis'; diff --git a/x-pack/plugins/inference/common/output/create_output_api.ts b/x-pack/plugins/inference/common/output/create_output_api.ts deleted file mode 100644 index 848135beefb0f..0000000000000 --- a/x-pack/plugins/inference/common/output/create_output_api.ts +++ /dev/null @@ -1,61 +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 { map } from 'rxjs'; -import { ChatCompleteAPI, ChatCompletionEventType, MessageRole } from '../chat_complete'; -import { withoutTokenCountEvents } from '../chat_complete/without_token_count_events'; -import { OutputAPI, OutputEvent, OutputEventType } from '.'; -import { ensureMultiTurn } from '../ensure_multi_turn'; - -export function createOutputApi(chatCompleteApi: ChatCompleteAPI): OutputAPI { - return (id, { connectorId, input, schema, system, previousMessages, functionCalling }) => { - return chatCompleteApi({ - connectorId, - system, - functionCalling, - messages: ensureMultiTurn([ - ...(previousMessages || []), - { - role: MessageRole.User, - content: input, - }, - ]), - ...(schema - ? { - tools: { - structuredOutput: { - description: `Use the following schema to respond to the user's request in structured data, so it can be parsed and handled.`, - schema, - }, - }, - toolChoice: { function: 'structuredOutput' as const }, - } - : {}), - }).pipe( - withoutTokenCountEvents(), - map((event): OutputEvent => { - if (event.type === ChatCompletionEventType.ChatCompletionChunk) { - return { - type: OutputEventType.OutputUpdate, - id, - content: event.content, - }; - } - - return { - id, - output: - event.toolCalls.length && 'arguments' in event.toolCalls[0].function - ? event.toolCalls[0].function.arguments - : undefined, - content: event.content, - type: OutputEventType.OutputComplete, - }; - }) - ); - }; -} diff --git a/x-pack/plugins/inference/common/output/index.ts b/x-pack/plugins/inference/common/output/index.ts deleted file mode 100644 index 0f7655f8f1cd4..0000000000000 --- a/x-pack/plugins/inference/common/output/index.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { Observable } from 'rxjs'; -import { ServerSentEventBase } from '@kbn/sse-utils'; -import { FromToolSchema, ToolSchema } from '../chat_complete/tool_schema'; -import type { Message, FunctionCallingMode } from '../chat_complete'; - -export enum OutputEventType { - OutputUpdate = 'output', - OutputComplete = 'complete', -} - -type Output = Record | undefined | unknown; - -export type OutputUpdateEvent = ServerSentEventBase< - OutputEventType.OutputUpdate, - { - id: TId; - content: string; - } ->; - -export type OutputCompleteEvent< - TId extends string = string, - TOutput extends Output = Output -> = ServerSentEventBase< - OutputEventType.OutputComplete, - { - id: TId; - output: TOutput; - content: string; - } ->; - -export type OutputEvent = - | OutputUpdateEvent - | OutputCompleteEvent; - -/** - * Generate a response with the LLM for a prompt, optionally based on a schema. - * - * @param {string} id The id of the operation - * @param {string} options.connectorId The ID of the connector that is to be used. - * @param {string} options.input The prompt for the LLM. - * @param {string} options.messages Previous messages in a conversation. - * @param {ToolSchema} [options.schema] The schema the response from the LLM should adhere to. - */ -export type OutputAPI = < - TId extends string = string, - TOutputSchema extends ToolSchema | undefined = ToolSchema | undefined ->( - id: TId, - options: { - connectorId: string; - system?: string; - input: string; - schema?: TOutputSchema; - previousMessages?: Message[]; - functionCalling?: FunctionCallingMode; - } -) => Observable< - OutputEvent : undefined> ->; - -export function createOutputCompleteEvent( - id: TId, - output: TOutput, - content?: string -): OutputCompleteEvent { - return { - type: OutputEventType.OutputComplete, - id, - output, - content: content ?? '', - }; -} diff --git a/x-pack/plugins/inference/common/output/is_output_complete_event.ts b/x-pack/plugins/inference/common/output/is_output_complete_event.ts deleted file mode 100644 index bac3443b8258c..0000000000000 --- a/x-pack/plugins/inference/common/output/is_output_complete_event.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { OutputEvent, OutputEventType, OutputUpdateEvent } from '.'; - -export function isOutputCompleteEvent( - event: TOutputEvent -): event is Exclude { - return event.type === OutputEventType.OutputComplete; -} diff --git a/x-pack/plugins/inference/common/output/is_output_event.ts b/x-pack/plugins/inference/common/output/is_output_event.ts deleted file mode 100644 index dad2b0967a6ac..0000000000000 --- a/x-pack/plugins/inference/common/output/is_output_event.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { OutputEvent, OutputEventType } from '.'; -import type { InferenceTaskEvent } from '../inference_task'; - -export function isOutputEvent(event: InferenceTaskEvent): event is OutputEvent { - return ( - event.type === OutputEventType.OutputComplete || event.type === OutputEventType.OutputUpdate - ); -} diff --git a/x-pack/plugins/inference/common/output/is_output_update_event.ts b/x-pack/plugins/inference/common/output/is_output_update_event.ts deleted file mode 100644 index 459436e64014e..0000000000000 --- a/x-pack/plugins/inference/common/output/is_output_update_event.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { OutputEvent, OutputEventType, OutputUpdateEvent } from '.'; - -export function isOutputUpdateEvent( - event: OutputEvent -): event is OutputUpdateEvent { - return event.type === OutputEventType.OutputComplete; -} diff --git a/x-pack/plugins/inference/common/output/without_output_update_events.ts b/x-pack/plugins/inference/common/output/without_output_update_events.ts deleted file mode 100644 index 38f26c8c8ece1..0000000000000 --- a/x-pack/plugins/inference/common/output/without_output_update_events.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 { filter, OperatorFunction } from 'rxjs'; -import { OutputEvent, OutputEventType, OutputUpdateEvent } from '.'; - -export function withoutOutputUpdateEvents(): OperatorFunction< - T, - Exclude -> { - return filter( - (event): event is Exclude => event.type !== OutputEventType.OutputUpdate - ); -} diff --git a/x-pack/plugins/inference/common/tasks/nl_to_esql/correct_query_with_actions.ts b/x-pack/plugins/inference/common/tasks/nl_to_esql/correct_query_with_actions.ts index 15b050c3a3897..30e2c11adb6de 100644 --- a/x-pack/plugins/inference/common/tasks/nl_to_esql/correct_query_with_actions.ts +++ b/x-pack/plugins/inference/common/tasks/nl_to_esql/correct_query_with_actions.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import { validateQuery, getActions } from '@kbn/esql-validation-autocomplete'; import { getAstAndSyntaxErrors } from '@kbn/esql-ast'; diff --git a/x-pack/plugins/inference/common/ensure_multi_turn.ts b/x-pack/plugins/inference/common/utils/ensure_multi_turn.ts similarity index 92% rename from x-pack/plugins/inference/common/ensure_multi_turn.ts rename to x-pack/plugins/inference/common/utils/ensure_multi_turn.ts index 8d222564f3e72..476ecec108e94 100644 --- a/x-pack/plugins/inference/common/ensure_multi_turn.ts +++ b/x-pack/plugins/inference/common/utils/ensure_multi_turn.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Message, MessageRole } from './chat_complete'; +import { Message, MessageRole } from '@kbn/inference-common'; function isUserMessage(message: Message): boolean { return message.role !== MessageRole.Assistant; diff --git a/x-pack/plugins/inference/common/chat_complete/generate_fake_tool_call_id.ts b/x-pack/plugins/inference/common/utils/generate_fake_tool_call_id.ts similarity index 100% rename from x-pack/plugins/inference/common/chat_complete/generate_fake_tool_call_id.ts rename to x-pack/plugins/inference/common/utils/generate_fake_tool_call_id.ts diff --git a/x-pack/plugins/inference/common/util/truncate_list.ts b/x-pack/plugins/inference/common/utils/truncate_list.ts similarity index 100% rename from x-pack/plugins/inference/common/util/truncate_list.ts rename to x-pack/plugins/inference/common/utils/truncate_list.ts diff --git a/x-pack/plugins/inference/public/chat_complete.test.ts b/x-pack/plugins/inference/public/chat_complete.test.ts new file mode 100644 index 0000000000000..b297db1f2fb2c --- /dev/null +++ b/x-pack/plugins/inference/public/chat_complete.test.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { omit } from 'lodash'; +import { httpServiceMock } from '@kbn/core/public/mocks'; +import { ChatCompleteAPI, MessageRole, ChatCompleteOptions } from '@kbn/inference-common'; +import { createChatCompleteApi } from './chat_complete'; + +describe('createChatCompleteApi', () => { + let http: ReturnType; + let chatComplete: ChatCompleteAPI; + + beforeEach(() => { + http = httpServiceMock.createStartContract(); + chatComplete = createChatCompleteApi({ http }); + }); + + it('calls http.post with the right parameters when stream is not true', async () => { + const params = { + connectorId: 'my-connector', + functionCalling: 'native', + system: 'system', + messages: [{ role: MessageRole.User, content: 'question' }], + }; + await chatComplete(params as ChatCompleteOptions); + + expect(http.post).toHaveBeenCalledTimes(1); + expect(http.post).toHaveBeenCalledWith('/internal/inference/chat_complete', { + body: expect.any(String), + }); + const callBody = http.post.mock.lastCall!; + + expect(JSON.parse((callBody as any[])[1].body as string)).toEqual(params); + }); + + it('calls http.post with the right parameters when stream is true', async () => { + http.post.mockResolvedValue({}); + + const params = { + connectorId: 'my-connector', + functionCalling: 'native', + stream: true, + system: 'system', + messages: [{ role: MessageRole.User, content: 'question' }], + }; + + await chatComplete(params as ChatCompleteOptions); + + expect(http.post).toHaveBeenCalledTimes(1); + expect(http.post).toHaveBeenCalledWith('/internal/inference/chat_complete/stream', { + asResponse: true, + rawResponse: true, + body: expect.any(String), + }); + const callBody = http.post.mock.lastCall!; + + expect(JSON.parse((callBody as any[])[1].body as string)).toEqual(omit(params, 'stream')); + }); +}); diff --git a/x-pack/plugins/inference/public/chat_complete.ts b/x-pack/plugins/inference/public/chat_complete.ts new file mode 100644 index 0000000000000..64cb5533f20be --- /dev/null +++ b/x-pack/plugins/inference/public/chat_complete.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 type { HttpStart } from '@kbn/core/public'; +import { + ChatCompleteAPI, + ChatCompleteCompositeResponse, + ChatCompleteOptions, + ToolOptions, +} from '@kbn/inference-common'; +import { from } from 'rxjs'; +import type { ChatCompleteRequestBody } from '../common/http_apis'; +import { httpResponseIntoObservable } from './util/http_response_into_observable'; + +export function createChatCompleteApi({ http }: { http: HttpStart }): ChatCompleteAPI; +export function createChatCompleteApi({ http }: { http: HttpStart }) { + return ({ + connectorId, + messages, + system, + toolChoice, + tools, + functionCalling, + stream, + }: ChatCompleteOptions): ChatCompleteCompositeResponse< + ToolOptions, + boolean + > => { + const body: ChatCompleteRequestBody = { + connectorId, + system, + messages, + toolChoice, + tools, + functionCalling, + }; + + if (stream) { + return from( + http.post('/internal/inference/chat_complete/stream', { + asResponse: true, + rawResponse: true, + body: JSON.stringify(body), + }) + ).pipe(httpResponseIntoObservable()); + } else { + return http.post('/internal/inference/chat_complete', { + body: JSON.stringify(body), + }); + } + }; +} diff --git a/x-pack/plugins/inference/public/chat_complete/index.ts b/x-pack/plugins/inference/public/chat_complete/index.ts deleted file mode 100644 index e229f6c8f8eae..0000000000000 --- a/x-pack/plugins/inference/public/chat_complete/index.ts +++ /dev/null @@ -1,33 +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 { from } from 'rxjs'; -import type { HttpStart } from '@kbn/core/public'; -import type { ChatCompleteAPI } from '../../common/chat_complete'; -import type { ChatCompleteRequestBody } from '../../common/chat_complete/request'; -import { httpResponseIntoObservable } from '../util/http_response_into_observable'; - -export function createChatCompleteApi({ http }: { http: HttpStart }): ChatCompleteAPI { - return ({ connectorId, messages, system, toolChoice, tools, functionCalling }) => { - const body: ChatCompleteRequestBody = { - connectorId, - system, - messages, - toolChoice, - tools, - functionCalling, - }; - - return from( - http.post('/internal/inference/chat_complete', { - asResponse: true, - rawResponse: true, - body: JSON.stringify(body), - }) - ).pipe(httpResponseIntoObservable()); - }; -} diff --git a/x-pack/plugins/inference/public/index.ts b/x-pack/plugins/inference/public/index.ts index 82d36a7abe82d..4928242879b3b 100644 --- a/x-pack/plugins/inference/public/index.ts +++ b/x-pack/plugins/inference/public/index.ts @@ -4,9 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { PluginInitializer, PluginInitializerContext } from '@kbn/core/public'; -import { InferencePlugin } from './plugin'; +import type { PluginInitializer, PluginInitializerContext } from '@kbn/core/public'; import type { InferencePublicSetup, InferencePublicStart, @@ -14,6 +13,7 @@ import type { InferenceStartDependencies, ConfigSchema, } from './types'; +import { InferencePlugin } from './plugin'; export { httpResponseIntoObservable } from './util/http_response_into_observable'; diff --git a/x-pack/plugins/inference/public/plugin.tsx b/x-pack/plugins/inference/public/plugin.tsx index 13ef4a0373845..f1023bc9c2546 100644 --- a/x-pack/plugins/inference/public/plugin.tsx +++ b/x-pack/plugins/inference/public/plugin.tsx @@ -7,8 +7,8 @@ import type { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public'; import type { Logger } from '@kbn/logging'; -import { createOutputApi } from '../common/output/create_output_api'; -import type { GetConnectorsResponseBody } from '../common/connectors'; +import { createOutputApi } from '../common/create_output_api'; +import type { GetConnectorsResponseBody } from '../common/http_apis'; import { createChatCompleteApi } from './chat_complete'; import type { ConfigSchema, @@ -41,10 +41,11 @@ export class InferencePlugin start(coreStart: CoreStart, pluginsStart: InferenceStartDependencies): InferencePublicStart { const chatComplete = createChatCompleteApi({ http: coreStart.http }); + const output = createOutputApi(chatComplete); return { chatComplete, - output: createOutputApi(chatComplete), + output, getConnectors: async () => { const res = await coreStart.http.get( '/internal/inference/connectors' diff --git a/x-pack/plugins/inference/public/types.ts b/x-pack/plugins/inference/public/types.ts index df80256679ab4..735abfb5459a0 100644 --- a/x-pack/plugins/inference/public/types.ts +++ b/x-pack/plugins/inference/public/types.ts @@ -4,9 +4,9 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { ChatCompleteAPI } from '../common/chat_complete'; + +import type { ChatCompleteAPI, OutputAPI } from '@kbn/inference-common'; import type { InferenceConnector } from '../common/connectors'; -import type { OutputAPI } from '../common/output'; /* eslint-disable @typescript-eslint/no-empty-interface*/ diff --git a/x-pack/plugins/inference/public/util/create_observable_from_http_response.ts b/x-pack/plugins/inference/public/util/create_observable_from_http_response.ts index 09e9b9b2d5f5e..862986ce1c73a 100644 --- a/x-pack/plugins/inference/public/util/create_observable_from_http_response.ts +++ b/x-pack/plugins/inference/public/util/create_observable_from_http_response.ts @@ -4,9 +4,10 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import { createParser } from 'eventsource-parser'; import { Observable, throwError } from 'rxjs'; -import { createInferenceInternalError } from '../../common/errors'; +import { createInferenceInternalError } from '@kbn/inference-common'; export interface StreamedHttpResponse { response?: { body: ReadableStream | null | undefined }; diff --git a/x-pack/plugins/inference/public/util/http_response_into_observable.test.ts b/x-pack/plugins/inference/public/util/http_response_into_observable.test.ts index 2b99b6f1db6f5..a0964da025af8 100644 --- a/x-pack/plugins/inference/public/util/http_response_into_observable.test.ts +++ b/x-pack/plugins/inference/public/util/http_response_into_observable.test.ts @@ -7,10 +7,12 @@ import { lastValueFrom, of, toArray } from 'rxjs'; import { httpResponseIntoObservable } from './http_response_into_observable'; +import { + ChatCompletionEventType, + InferenceTaskEventType, + InferenceTaskErrorCode, +} from '@kbn/inference-common'; import type { StreamedHttpResponse } from './create_observable_from_http_response'; -import { ChatCompletionEventType } from '../../common/chat_complete'; -import { InferenceTaskEventType } from '../../common/inference_task'; -import { InferenceTaskErrorCode } from '../../common/errors'; function toSse(...events: Array>) { return events.map((event) => new TextEncoder().encode(`data: ${JSON.stringify(event)}\n\n`)); diff --git a/x-pack/plugins/inference/public/util/http_response_into_observable.ts b/x-pack/plugins/inference/public/util/http_response_into_observable.ts index c63a7bcb3cd15..0aab09cdebe0c 100644 --- a/x-pack/plugins/inference/public/util/http_response_into_observable.ts +++ b/x-pack/plugins/inference/public/util/http_response_into_observable.ts @@ -10,8 +10,9 @@ import { createInferenceInternalError, InferenceTaskError, InferenceTaskErrorEvent, -} from '../../common/errors'; -import { InferenceTaskEvent, InferenceTaskEventType } from '../../common/inference_task'; + InferenceTaskEvent, + InferenceTaskEventType, +} from '@kbn/inference-common'; import { createObservableFromHttpResponse, StreamedHttpResponse, diff --git a/x-pack/plugins/inference/scripts/evaluation/evaluation.ts b/x-pack/plugins/inference/scripts/evaluation/evaluation.ts index 0c70bf81eddad..425b7e334074a 100644 --- a/x-pack/plugins/inference/scripts/evaluation/evaluation.ts +++ b/x-pack/plugins/inference/scripts/evaluation/evaluation.ts @@ -89,8 +89,8 @@ function runEvaluations() { const evaluationClient = createInferenceEvaluationClient({ connectorId: connector.connectorId, evaluationConnectorId, - outputApi: (id, parameters) => - chatClient.output(id, { + outputApi: (parameters) => + chatClient.output({ ...parameters, connectorId: evaluationConnectorId, }) as any, diff --git a/x-pack/plugins/inference/scripts/evaluation/evaluation_client.ts b/x-pack/plugins/inference/scripts/evaluation/evaluation_client.ts index acf2fece1d0ff..99eaf02494af7 100644 --- a/x-pack/plugins/inference/scripts/evaluation/evaluation_client.ts +++ b/x-pack/plugins/inference/scripts/evaluation/evaluation_client.ts @@ -6,9 +6,7 @@ */ import { remove } from 'lodash'; -import { lastValueFrom } from 'rxjs'; -import type { OutputAPI } from '../../common/output'; -import { withoutOutputUpdateEvents } from '../../common/output/without_output_update_events'; +import type { OutputAPI } from '@kbn/inference-common'; import type { EvaluationResult } from './types'; export interface InferenceEvaluationClient { @@ -68,11 +66,12 @@ export function createInferenceEvaluationClient({ output: outputApi, getEvaluationConnectorId: () => evaluationConnectorId, evaluate: async ({ input, criteria = [], system }) => { - const evaluation = await lastValueFrom( - outputApi('evaluate', { - connectorId, - system: withAdditionalSystemContext( - `You are a helpful, respected assistant for evaluating task + const evaluation = await outputApi({ + id: 'evaluate', + stream: false, + connectorId, + system: withAdditionalSystemContext( + `You are a helpful, respected assistant for evaluating task inputs and outputs in the Elastic Platform. Your goal is to verify whether the output of a task @@ -85,10 +84,10 @@ export function createInferenceEvaluationClient({ quoting what the assistant did wrong, where it could improve, and what the root cause was in case of a failure. `, - system - ), + system + ), - input: ` + input: ` ## Criteria ${criteria @@ -100,37 +99,36 @@ export function createInferenceEvaluationClient({ ## Input ${input}`, - schema: { - type: 'object', - properties: { - criteria: { - type: 'array', - items: { - type: 'object', - properties: { - index: { - type: 'number', - description: 'The number of the criterion', - }, - score: { - type: 'number', - description: - 'The score you calculated for the criterion, between 0 (criterion fully failed) and 1 (criterion fully succeeded).', - }, - reasoning: { - type: 'string', - description: - 'Your reasoning for the score. Explain your score by mentioning what you expected to happen and what did happen.', - }, + schema: { + type: 'object', + properties: { + criteria: { + type: 'array', + items: { + type: 'object', + properties: { + index: { + type: 'number', + description: 'The number of the criterion', + }, + score: { + type: 'number', + description: + 'The score you calculated for the criterion, between 0 (criterion fully failed) and 1 (criterion fully succeeded).', + }, + reasoning: { + type: 'string', + description: + 'Your reasoning for the score. Explain your score by mentioning what you expected to happen and what did happen.', }, - required: ['index', 'score', 'reasoning'], }, + required: ['index', 'score', 'reasoning'], }, }, - required: ['criteria'], - } as const, - }).pipe(withoutOutputUpdateEvents()) - ); + }, + required: ['criteria'], + } as const, + }); const scoredCriteria = evaluation.output.criteria; diff --git a/x-pack/plugins/inference/scripts/evaluation/scenarios/esql/index.spec.ts b/x-pack/plugins/inference/scripts/evaluation/scenarios/esql/index.spec.ts index 3aeca67030366..49a82db8124e9 100644 --- a/x-pack/plugins/inference/scripts/evaluation/scenarios/esql/index.spec.ts +++ b/x-pack/plugins/inference/scripts/evaluation/scenarios/esql/index.spec.ts @@ -8,11 +8,11 @@ /// import expect from '@kbn/expect'; -import { firstValueFrom, lastValueFrom, filter } from 'rxjs'; +import type { Logger } from '@kbn/logging'; +import { lastValueFrom } from 'rxjs'; import { naturalLanguageToEsql } from '../../../../server/tasks/nl_to_esql'; import { chatClient, evaluationClient, logger } from '../../services'; import { EsqlDocumentBase } from '../../../../server/tasks/nl_to_esql/doc_base'; -import { isOutputCompleteEvent } from '../../../../common'; interface TestCase { title: string; @@ -40,7 +40,7 @@ const callNaturalLanguageToEsql = async (question: string) => { debug: (source) => { logger.debug(typeof source === 'function' ? source() : source); }, - }, + } as Logger, }) ); }; @@ -65,11 +65,10 @@ const retrieveUsedCommands = async ({ answer: string; esqlDescription: string; }) => { - const commandsListOutput = await firstValueFrom( - evaluationClient - .output('retrieve_commands', { - connectorId: evaluationClient.getEvaluationConnectorId(), - system: ` + const commandsListOutput = await evaluationClient.output({ + id: 'retrieve_commands', + connectorId: evaluationClient.getEvaluationConnectorId(), + system: ` You are a helpful, respected Elastic ES|QL assistant. Your role is to enumerate the list of ES|QL commands and functions that were used @@ -81,34 +80,32 @@ const retrieveUsedCommands = async ({ ${esqlDescription} `, - input: ` + input: ` # Question ${question} # Answer ${answer} `, - schema: { - type: 'object', - properties: { - commands: { - description: - 'The list of commands that were used in the provided ES|QL question and answer', - type: 'array', - items: { type: 'string' }, - }, - functions: { - description: - 'The list of functions that were used in the provided ES|QL question and answer', - type: 'array', - items: { type: 'string' }, - }, - }, - required: ['commands', 'functions'], - } as const, - }) - .pipe(filter(isOutputCompleteEvent)) - ); + schema: { + type: 'object', + properties: { + commands: { + description: + 'The list of commands that were used in the provided ES|QL question and answer', + type: 'array', + items: { type: 'string' }, + }, + functions: { + description: + 'The list of functions that were used in the provided ES|QL question and answer', + type: 'array', + items: { type: 'string' }, + }, + }, + required: ['commands', 'functions'], + } as const, + }); const output = commandsListOutput.output; diff --git a/x-pack/plugins/inference/scripts/load_esql_docs/utils/output_executor.ts b/x-pack/plugins/inference/scripts/load_esql_docs/utils/output_executor.ts index 6697446f93cec..f4014db0e6e8d 100644 --- a/x-pack/plugins/inference/scripts/load_esql_docs/utils/output_executor.ts +++ b/x-pack/plugins/inference/scripts/load_esql_docs/utils/output_executor.ts @@ -5,8 +5,7 @@ * 2.0. */ -import { lastValueFrom } from 'rxjs'; -import type { OutputAPI } from '../../../common/output'; +import type { OutputAPI } from '@kbn/inference-common'; export interface Prompt { system?: string; @@ -27,13 +26,13 @@ export type PromptCallerFactory = ({ export const bindOutput: PromptCallerFactory = ({ connectorId, output }) => { return async ({ input, system }) => { - const response = await lastValueFrom( - output('', { - connectorId, - input, - system, - }) - ); + const response = await output({ + id: 'output', + connectorId, + input, + system, + }); + return response.content ?? ''; }; }; diff --git a/x-pack/plugins/inference/scripts/util/cli_options.ts b/x-pack/plugins/inference/scripts/util/cli_options.ts index 13bac131922ff..8bbb6dabe406e 100644 --- a/x-pack/plugins/inference/scripts/util/cli_options.ts +++ b/x-pack/plugins/inference/scripts/util/cli_options.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import { format, parse } from 'url'; import { readKibanaConfig } from './read_kibana_config'; diff --git a/x-pack/plugins/inference/scripts/util/kibana_client.ts b/x-pack/plugins/inference/scripts/util/kibana_client.ts index ca26ef76b2c72..ad6c21cf4b248 100644 --- a/x-pack/plugins/inference/scripts/util/kibana_client.ts +++ b/x-pack/plugins/inference/scripts/util/kibana_client.ts @@ -13,18 +13,22 @@ import { from, map, switchMap, throwError } from 'rxjs'; import { UrlObject, format, parse } from 'url'; import { inspect } from 'util'; import { isReadable } from 'stream'; -import type { ChatCompleteAPI, ChatCompletionEvent } from '../../common/chat_complete'; -import { ChatCompleteRequestBody } from '../../common/chat_complete/request'; -import type { InferenceConnector } from '../../common/connectors'; import { + ChatCompleteAPI, + ChatCompleteCompositeResponse, + OutputAPI, + ChatCompletionEvent, InferenceTaskError, InferenceTaskErrorEvent, + InferenceTaskEventType, createInferenceInternalError, -} from '../../common/errors'; -import { InferenceTaskEventType } from '../../common/inference_task'; -import type { OutputAPI } from '../../common/output'; -import { createOutputApi } from '../../common/output/create_output_api'; -import { withoutOutputUpdateEvents } from '../../common/output/without_output_update_events'; + withoutOutputUpdateEvents, + type ToolOptions, + ChatCompleteOptions, +} from '@kbn/inference-common'; +import type { ChatCompleteRequestBody } from '../../common/http_apis'; +import type { InferenceConnector } from '../../common/connectors'; +import { createOutputApi } from '../../common/create_output_api'; import { eventSourceStreamIntoObservable } from '../../server/util/event_source_stream_into_observable'; // eslint-disable-next-line spaced-comment @@ -153,7 +157,7 @@ export class KibanaClient { } createInferenceClient({ connectorId }: { connectorId: string }): ScriptInferenceClient { - function stream(responsePromise: Promise) { + function streamResponse(responsePromise: Promise) { return from(responsePromise).pipe( switchMap((response) => { if (isReadable(response.data)) { @@ -173,14 +177,18 @@ export class KibanaClient { ); } - const chatCompleteApi: ChatCompleteAPI = ({ + const chatCompleteApi: ChatCompleteAPI = < + TToolOptions extends ToolOptions = ToolOptions, + TStream extends boolean = false + >({ connectorId: chatCompleteConnectorId, messages, system, toolChoice, tools, functionCalling, - }) => { + stream, + }: ChatCompleteOptions) => { const body: ChatCompleteRequestBody = { connectorId: chatCompleteConnectorId, system, @@ -190,15 +198,29 @@ export class KibanaClient { functionCalling, }; - return stream( - this.axios.post( - this.getUrl({ - pathname: `/internal/inference/chat_complete`, - }), - body, - { responseType: 'stream', timeout: NaN } - ) - ); + if (stream) { + return streamResponse( + this.axios.post( + this.getUrl({ + pathname: `/internal/inference/chat_complete/stream`, + }), + body, + { responseType: 'stream', timeout: NaN } + ) + ) as ChatCompleteCompositeResponse; + } else { + return this.axios + .post( + this.getUrl({ + pathname: `/internal/inference/chat_complete/stream`, + }), + body, + { responseType: 'stream', timeout: NaN } + ) + .then((response) => { + return response.data; + }) as ChatCompleteCompositeResponse; + } }; const outputApi: OutputAPI = createOutputApi(chatCompleteApi); @@ -210,8 +232,13 @@ export class KibanaClient { ...options, }); }, - output: (id, options) => { - return outputApi(id, { ...options }).pipe(withoutOutputUpdateEvents()); + output: (options) => { + const response = outputApi({ ...options }); + if (options.stream) { + return (response as any).pipe(withoutOutputUpdateEvents()); + } else { + return response; + } }, }; } diff --git a/x-pack/plugins/inference/server/chat_complete/adapters/bedrock/bedrock_claude_adapter.test.ts b/x-pack/plugins/inference/server/chat_complete/adapters/bedrock/bedrock_claude_adapter.test.ts index d34b8693cb85f..ca6f60dd45a55 100644 --- a/x-pack/plugins/inference/server/chat_complete/adapters/bedrock/bedrock_claude_adapter.test.ts +++ b/x-pack/plugins/inference/server/chat_complete/adapters/bedrock/bedrock_claude_adapter.test.ts @@ -8,8 +8,7 @@ import { PassThrough } from 'stream'; import { loggerMock } from '@kbn/logging-mocks'; import type { InferenceExecutor } from '../../utils/inference_executor'; -import { MessageRole } from '../../../../common/chat_complete'; -import { ToolChoiceType } from '../../../../common/chat_complete/tools'; +import { MessageRole, ToolChoiceType } from '@kbn/inference-common'; import { bedrockClaudeAdapter } from './bedrock_claude_adapter'; import { addNoToolUsageDirective } from './prompts'; diff --git a/x-pack/plugins/inference/server/chat_complete/adapters/bedrock/bedrock_claude_adapter.ts b/x-pack/plugins/inference/server/chat_complete/adapters/bedrock/bedrock_claude_adapter.ts index a0b48e6fc8631..e73d9c9344c98 100644 --- a/x-pack/plugins/inference/server/chat_complete/adapters/bedrock/bedrock_claude_adapter.ts +++ b/x-pack/plugins/inference/server/chat_complete/adapters/bedrock/bedrock_claude_adapter.ts @@ -7,10 +7,15 @@ import { filter, from, map, switchMap, tap } from 'rxjs'; import { Readable } from 'stream'; +import { + Message, + MessageRole, + createInferenceInternalError, + ToolChoiceType, + ToolSchemaType, + type ToolOptions, +} from '@kbn/inference-common'; import { parseSerdeChunkMessage } from './serde_utils'; -import { Message, MessageRole } from '../../../../common/chat_complete'; -import { createInferenceInternalError } from '../../../../common/errors'; -import { ToolChoiceType, type ToolOptions } from '../../../../common/chat_complete/tools'; import { InferenceConnectorAdapter } from '../../types'; import type { BedRockMessage, BedrockToolChoice } from './types'; import { @@ -19,7 +24,6 @@ import { } from './serde_eventstream_into_observable'; import { processCompletionChunks } from './process_completion_chunks'; import { addNoToolUsageDirective } from './prompts'; -import { ToolSchemaType } from '../../../../common/chat_complete/tool_schema'; export const bedrockClaudeAdapter: InferenceConnectorAdapter = { chatComplete: ({ executor, system, messages, toolChoice, tools }) => { diff --git a/x-pack/plugins/inference/server/chat_complete/adapters/bedrock/process_completion_chunks.ts b/x-pack/plugins/inference/server/chat_complete/adapters/bedrock/process_completion_chunks.ts index 5513cc9028ac9..8a5c9805ddf63 100644 --- a/x-pack/plugins/inference/server/chat_complete/adapters/bedrock/process_completion_chunks.ts +++ b/x-pack/plugins/inference/server/chat_complete/adapters/bedrock/process_completion_chunks.ts @@ -11,7 +11,7 @@ import { ChatCompletionTokenCountEvent, ChatCompletionChunkToolCall, ChatCompletionEventType, -} from '../../../../common/chat_complete'; +} from '@kbn/inference-common'; import type { CompletionChunk, MessageStopChunk } from './types'; export function processCompletionChunks() { diff --git a/x-pack/plugins/inference/server/chat_complete/adapters/bedrock/serde_eventstream_into_observable.ts b/x-pack/plugins/inference/server/chat_complete/adapters/bedrock/serde_eventstream_into_observable.ts index 24a245ab2efcc..5ab264750e5a9 100644 --- a/x-pack/plugins/inference/server/chat_complete/adapters/bedrock/serde_eventstream_into_observable.ts +++ b/x-pack/plugins/inference/server/chat_complete/adapters/bedrock/serde_eventstream_into_observable.ts @@ -11,7 +11,7 @@ import { identity } from 'lodash'; import { Observable } from 'rxjs'; import { Readable } from 'stream'; import { Message } from '@smithy/types'; -import { createInferenceInternalError } from '../../../../common/errors'; +import { createInferenceInternalError } from '@kbn/inference-common'; interface ModelStreamErrorException { name: 'ModelStreamErrorException'; diff --git a/x-pack/plugins/inference/server/chat_complete/adapters/gemini/gemini_adapter.test.ts b/x-pack/plugins/inference/server/chat_complete/adapters/gemini/gemini_adapter.test.ts index a9f4305a3c532..c3410b2af3623 100644 --- a/x-pack/plugins/inference/server/chat_complete/adapters/gemini/gemini_adapter.test.ts +++ b/x-pack/plugins/inference/server/chat_complete/adapters/gemini/gemini_adapter.test.ts @@ -11,8 +11,7 @@ import { noop, tap, lastValueFrom, toArray, Subject } from 'rxjs'; import { loggerMock } from '@kbn/logging-mocks'; import type { InferenceExecutor } from '../../utils/inference_executor'; import { observableIntoEventSourceStream } from '../../../util/observable_into_event_source_stream'; -import { MessageRole } from '../../../../common/chat_complete'; -import { ToolChoiceType } from '../../../../common/chat_complete/tools'; +import { MessageRole, ToolChoiceType } from '@kbn/inference-common'; import { geminiAdapter } from './gemini_adapter'; describe('geminiAdapter', () => { diff --git a/x-pack/plugins/inference/server/chat_complete/adapters/gemini/gemini_adapter.ts b/x-pack/plugins/inference/server/chat_complete/adapters/gemini/gemini_adapter.ts index 2e86adcc82a85..80d0439449066 100644 --- a/x-pack/plugins/inference/server/chat_complete/adapters/gemini/gemini_adapter.ts +++ b/x-pack/plugins/inference/server/chat_complete/adapters/gemini/gemini_adapter.ts @@ -8,10 +8,15 @@ import * as Gemini from '@google/generative-ai'; import { from, map, switchMap } from 'rxjs'; import { Readable } from 'stream'; +import { + Message, + MessageRole, + ToolChoiceType, + ToolOptions, + ToolSchema, + ToolSchemaType, +} from '@kbn/inference-common'; import type { InferenceConnectorAdapter } from '../../types'; -import { Message, MessageRole } from '../../../../common/chat_complete'; -import { ToolChoiceType, ToolOptions } from '../../../../common/chat_complete/tools'; -import type { ToolSchema, ToolSchemaType } from '../../../../common/chat_complete/tool_schema'; import { eventSourceStreamIntoObservable } from '../../../util/event_source_stream_into_observable'; import { processVertexStream } from './process_vertex_stream'; import type { GenerateContentResponseChunk, GeminiMessage, GeminiToolConfig } from './types'; diff --git a/x-pack/plugins/inference/server/chat_complete/adapters/gemini/process_vertex_stream.test.ts b/x-pack/plugins/inference/server/chat_complete/adapters/gemini/process_vertex_stream.test.ts index 78e0da0a384b8..8613799846e3b 100644 --- a/x-pack/plugins/inference/server/chat_complete/adapters/gemini/process_vertex_stream.test.ts +++ b/x-pack/plugins/inference/server/chat_complete/adapters/gemini/process_vertex_stream.test.ts @@ -6,7 +6,7 @@ */ import { TestScheduler } from 'rxjs/testing'; -import { ChatCompletionEventType } from '../../../../common/chat_complete'; +import { ChatCompletionEventType } from '@kbn/inference-common'; import { processVertexStream } from './process_vertex_stream'; import type { GenerateContentResponseChunk } from './types'; diff --git a/x-pack/plugins/inference/server/chat_complete/adapters/gemini/process_vertex_stream.ts b/x-pack/plugins/inference/server/chat_complete/adapters/gemini/process_vertex_stream.ts index e2a6c74a0447f..3081317882c65 100644 --- a/x-pack/plugins/inference/server/chat_complete/adapters/gemini/process_vertex_stream.ts +++ b/x-pack/plugins/inference/server/chat_complete/adapters/gemini/process_vertex_stream.ts @@ -10,7 +10,7 @@ import { ChatCompletionChunkEvent, ChatCompletionTokenCountEvent, ChatCompletionEventType, -} from '../../../../common/chat_complete'; +} from '@kbn/inference-common'; import { generateFakeToolCallId } from '../../../../common'; import type { GenerateContentResponseChunk } from './types'; diff --git a/x-pack/plugins/inference/server/chat_complete/adapters/openai/openai_adapter.test.ts b/x-pack/plugins/inference/server/chat_complete/adapters/openai/openai_adapter.test.ts index 813e88760de8c..ff1bbc71a876d 100644 --- a/x-pack/plugins/inference/server/chat_complete/adapters/openai/openai_adapter.test.ts +++ b/x-pack/plugins/inference/server/chat_complete/adapters/openai/openai_adapter.test.ts @@ -12,7 +12,7 @@ import { pick } from 'lodash'; import { lastValueFrom, Subject, toArray } from 'rxjs'; import type { Logger } from '@kbn/logging'; import { loggerMock } from '@kbn/logging-mocks'; -import { ChatCompletionEventType, MessageRole } from '../../../../common/chat_complete'; +import { ChatCompletionEventType, MessageRole } from '@kbn/inference-common'; import { observableIntoEventSourceStream } from '../../../util/observable_into_event_source_stream'; import { InferenceExecutor } from '../../utils/inference_executor'; import { openAIAdapter } from '.'; diff --git a/x-pack/plugins/inference/server/chat_complete/adapters/openai/openai_adapter.ts b/x-pack/plugins/inference/server/chat_complete/adapters/openai/openai_adapter.ts index f1821be4d4d57..121ba96ab115a 100644 --- a/x-pack/plugins/inference/server/chat_complete/adapters/openai/openai_adapter.ts +++ b/x-pack/plugins/inference/server/chat_complete/adapters/openai/openai_adapter.ts @@ -20,10 +20,10 @@ import { ChatCompletionEventType, Message, MessageRole, -} from '../../../../common/chat_complete'; -import type { ToolOptions } from '../../../../common/chat_complete/tools'; -import { createTokenLimitReachedError } from '../../../../common/chat_complete/errors'; -import { createInferenceInternalError } from '../../../../common/errors'; + ToolOptions, + createInferenceInternalError, +} from '@kbn/inference-common'; +import { createTokenLimitReachedError } from '../../errors'; import { eventSourceStreamIntoObservable } from '../../../util/event_source_stream_into_observable'; import type { InferenceConnectorAdapter } from '../../types'; import { diff --git a/x-pack/plugins/inference/server/chat_complete/api.ts b/x-pack/plugins/inference/server/chat_complete/api.ts index ca9e61ff3627f..cf325e72ddf3a 100644 --- a/x-pack/plugins/inference/server/chat_complete/api.ts +++ b/x-pack/plugins/inference/server/chat_complete/api.ts @@ -9,31 +9,39 @@ import { last } from 'lodash'; import { defer, switchMap, throwError } from 'rxjs'; import type { Logger } from '@kbn/logging'; import type { KibanaRequest } from '@kbn/core-http-server'; -import type { ChatCompleteAPI, ChatCompletionResponse } from '../../common/chat_complete'; -import { createInferenceRequestError } from '../../common/errors'; +import { + type ChatCompleteAPI, + type ChatCompleteCompositeResponse, + createInferenceRequestError, + type ToolOptions, + ChatCompleteOptions, +} from '@kbn/inference-common'; import type { InferenceStartDependencies } from '../types'; import { getConnectorById } from '../util/get_connector_by_id'; import { getInferenceAdapter } from './adapters'; -import { createInferenceExecutor, chunksIntoMessage } from './utils'; +import { createInferenceExecutor, chunksIntoMessage, streamToResponse } from './utils'; -export function createChatCompleteApi({ - request, - actions, - logger, -}: { +interface CreateChatCompleteApiOptions { request: KibanaRequest; actions: InferenceStartDependencies['actions']; logger: Logger; -}) { - const chatCompleteAPI: ChatCompleteAPI = ({ +} + +export function createChatCompleteApi(options: CreateChatCompleteApiOptions): ChatCompleteAPI; +export function createChatCompleteApi({ request, actions, logger }: CreateChatCompleteApiOptions) { + return ({ connectorId, messages, toolChoice, tools, system, functionCalling, - }): ChatCompletionResponse => { - return defer(async () => { + stream, + }: ChatCompleteOptions): ChatCompleteCompositeResponse< + ToolOptions, + boolean + > => { + const obs$ = defer(async () => { const actionsClient = await actions.getActionsClientWithRequest(request); const connector = await getConnectorById({ connectorId, actionsClient }); const executor = createInferenceExecutor({ actionsClient, connector }); @@ -70,7 +78,11 @@ export function createChatCompleteApi({ logger, }) ); - }; - return chatCompleteAPI; + if (stream) { + return obs$; + } else { + return streamToResponse(obs$); + } + }; } diff --git a/x-pack/plugins/inference/server/chat_complete/errors.ts b/x-pack/plugins/inference/server/chat_complete/errors.ts new file mode 100644 index 0000000000000..a830f57fec559 --- /dev/null +++ b/x-pack/plugins/inference/server/chat_complete/errors.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 { InferenceTaskError, type UnvalidatedToolCall } from '@kbn/inference-common'; +import { i18n } from '@kbn/i18n'; +import { + ChatCompletionErrorCode, + ChatCompletionTokenLimitReachedError, + ChatCompletionToolNotFoundError, + ChatCompletionToolValidationError, +} from '@kbn/inference-common/src/chat_complete/errors'; + +export function createTokenLimitReachedError( + tokenLimit?: number, + tokenCount?: number +): ChatCompletionTokenLimitReachedError { + return new InferenceTaskError( + ChatCompletionErrorCode.TokenLimitReachedError, + i18n.translate('xpack.inference.chatCompletionError.tokenLimitReachedError', { + defaultMessage: `Token limit reached. Token limit is {tokenLimit}, but the current conversation has {tokenCount} tokens.`, + values: { tokenLimit, tokenCount }, + }), + { tokenLimit, tokenCount } + ); +} + +export function createToolNotFoundError(name: string): ChatCompletionToolNotFoundError { + return new InferenceTaskError( + ChatCompletionErrorCode.ToolNotFoundError, + `Tool ${name} called but was not available`, + { + name, + } + ); +} + +export function createToolValidationError( + message: string, + meta: { + name?: string; + arguments?: string; + errorsText?: string; + toolCalls?: UnvalidatedToolCall[]; + } +): ChatCompletionToolValidationError { + return new InferenceTaskError(ChatCompletionErrorCode.ToolValidationError, message, meta); +} diff --git a/x-pack/plugins/inference/server/chat_complete/simulated_function_calling/get_system_instructions.ts b/x-pack/plugins/inference/server/chat_complete/simulated_function_calling/get_system_instructions.ts index abfc48dfa2ef2..c4adfae7e3f19 100644 --- a/x-pack/plugins/inference/server/chat_complete/simulated_function_calling/get_system_instructions.ts +++ b/x-pack/plugins/inference/server/chat_complete/simulated_function_calling/get_system_instructions.ts @@ -5,8 +5,8 @@ * 2.0. */ +import { ToolDefinition } from '@kbn/inference-common'; import { TOOL_USE_END, TOOL_USE_START } from './constants'; -import { ToolDefinition } from '../../../common/chat_complete/tools'; export function getSystemMessageInstructions({ tools, diff --git a/x-pack/plugins/inference/server/chat_complete/simulated_function_calling/parse_inline_function_calls.ts b/x-pack/plugins/inference/server/chat_complete/simulated_function_calling/parse_inline_function_calls.ts index 3436d7a7edac5..73d03ee2f00af 100644 --- a/x-pack/plugins/inference/server/chat_complete/simulated_function_calling/parse_inline_function_calls.ts +++ b/x-pack/plugins/inference/server/chat_complete/simulated_function_calling/parse_inline_function_calls.ts @@ -8,11 +8,11 @@ import { Observable } from 'rxjs'; import { Logger } from '@kbn/logging'; import { + createInferenceInternalError, ChatCompletionChunkEvent, ChatCompletionTokenCountEvent, ChatCompletionEventType, -} from '../../../common/chat_complete'; -import { createInferenceInternalError } from '../../../common/errors'; +} from '@kbn/inference-common'; import { TOOL_USE_END, TOOL_USE_START } from './constants'; function matchOnSignalStart(buffer: string) { diff --git a/x-pack/plugins/inference/server/chat_complete/simulated_function_calling/wrap_with_simulated_function_calling.ts b/x-pack/plugins/inference/server/chat_complete/simulated_function_calling/wrap_with_simulated_function_calling.ts index d8cfc373b66cc..4eb6cfd8d50e1 100644 --- a/x-pack/plugins/inference/server/chat_complete/simulated_function_calling/wrap_with_simulated_function_calling.ts +++ b/x-pack/plugins/inference/server/chat_complete/simulated_function_calling/wrap_with_simulated_function_calling.ts @@ -5,9 +5,16 @@ * 2.0. */ -import { AssistantMessage, Message, ToolMessage, UserMessage } from '../../../common'; -import { MessageRole } from '../../../common/chat_complete'; -import { ToolChoice, ToolChoiceType, ToolDefinition } from '../../../common/chat_complete/tools'; +import { + MessageRole, + AssistantMessage, + Message, + ToolMessage, + UserMessage, + ToolChoice, + ToolChoiceType, + ToolDefinition, +} from '@kbn/inference-common'; import { TOOL_USE_END, TOOL_USE_START } from './constants'; import { getSystemMessageInstructions } from './get_system_instructions'; diff --git a/x-pack/plugins/inference/server/chat_complete/types.ts b/x-pack/plugins/inference/server/chat_complete/types.ts index 394fe370240ef..64cc542ff6119 100644 --- a/x-pack/plugins/inference/server/chat_complete/types.ts +++ b/x-pack/plugins/inference/server/chat_complete/types.ts @@ -12,8 +12,8 @@ import type { ChatCompletionTokenCountEvent, FunctionCallingMode, Message, -} from '../../common/chat_complete'; -import type { ToolOptions } from '../../common/chat_complete/tools'; + ToolOptions, +} from '@kbn/inference-common'; import type { InferenceExecutor } from './utils'; /** diff --git a/x-pack/plugins/inference/server/chat_complete/utils/chunks_into_message.test.ts b/x-pack/plugins/inference/server/chat_complete/utils/chunks_into_message.test.ts index 0c5552a0113b8..c6e5b032120a3 100644 --- a/x-pack/plugins/inference/server/chat_complete/utils/chunks_into_message.test.ts +++ b/x-pack/plugins/inference/server/chat_complete/utils/chunks_into_message.test.ts @@ -4,13 +4,14 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import { lastValueFrom, of } from 'rxjs'; import { + ToolChoiceType, ChatCompletionChunkEvent, ChatCompletionEventType, ChatCompletionTokenCountEvent, -} from '../../../common/chat_complete'; -import { ToolChoiceType } from '../../../common/chat_complete/tools'; +} from '@kbn/inference-common'; import { chunksIntoMessage } from './chunks_into_message'; import type { Logger } from '@kbn/logging'; diff --git a/x-pack/plugins/inference/server/chat_complete/utils/chunks_into_message.ts b/x-pack/plugins/inference/server/chat_complete/utils/chunks_into_message.ts index 902289182a37a..fe9b745f442fc 100644 --- a/x-pack/plugins/inference/server/chat_complete/utils/chunks_into_message.ts +++ b/x-pack/plugins/inference/server/chat_complete/utils/chunks_into_message.ts @@ -7,14 +7,15 @@ import { last, map, merge, OperatorFunction, scan, share } from 'rxjs'; import type { Logger } from '@kbn/logging'; -import type { UnvalidatedToolCall, ToolOptions } from '../../../common/chat_complete/tools'; import { + UnvalidatedToolCall, + ToolOptions, ChatCompletionChunkEvent, ChatCompletionEventType, ChatCompletionMessageEvent, ChatCompletionTokenCountEvent, -} from '../../../common/chat_complete'; -import { withoutTokenCountEvents } from '../../../common/chat_complete/without_token_count_events'; + withoutTokenCountEvents, +} from '@kbn/inference-common'; import { validateToolCalls } from '../../util/validate_tool_calls'; export function chunksIntoMessage({ diff --git a/x-pack/plugins/inference/server/chat_complete/utils/index.ts b/x-pack/plugins/inference/server/chat_complete/utils/index.ts index dea2ac65f4755..d3dc2010cba3a 100644 --- a/x-pack/plugins/inference/server/chat_complete/utils/index.ts +++ b/x-pack/plugins/inference/server/chat_complete/utils/index.ts @@ -12,3 +12,4 @@ export { type InferenceExecutor, } from './inference_executor'; export { chunksIntoMessage } from './chunks_into_message'; +export { streamToResponse } from './stream_to_response'; diff --git a/x-pack/plugins/inference/server/chat_complete/utils/stream_to_response.test.ts b/x-pack/plugins/inference/server/chat_complete/utils/stream_to_response.test.ts new file mode 100644 index 0000000000000..939997a5fef15 --- /dev/null +++ b/x-pack/plugins/inference/server/chat_complete/utils/stream_to_response.test.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { of } from 'rxjs'; +import { ChatCompletionEvent } from '@kbn/inference-common'; +import { chunkEvent, tokensEvent, messageEvent } from '../../test_utils/chat_complete_events'; +import { streamToResponse } from './stream_to_response'; + +describe('streamToResponse', () => { + function fromEvents(...events: ChatCompletionEvent[]) { + return of(...events); + } + + it('returns a response with token count if both message and token events got emitted', async () => { + const response = await streamToResponse( + fromEvents( + chunkEvent('chunk_1'), + chunkEvent('chunk_2'), + tokensEvent({ prompt: 1, completion: 2, total: 3 }), + messageEvent('message') + ) + ); + + expect(response).toEqual({ + content: 'message', + tokens: { + completion: 2, + prompt: 1, + total: 3, + }, + toolCalls: [], + }); + }); + + it('returns a response with tool calls if present', async () => { + const someToolCall = { + toolCallId: '42', + function: { + name: 'my_tool', + arguments: {}, + }, + }; + const response = await streamToResponse( + fromEvents(chunkEvent('chunk_1'), messageEvent('message', [someToolCall])) + ); + + expect(response).toEqual({ + content: 'message', + toolCalls: [someToolCall], + }); + }); + + it('returns a response without token count if only message got emitted', async () => { + const response = await streamToResponse( + fromEvents(chunkEvent('chunk_1'), messageEvent('message')) + ); + + expect(response).toEqual({ + content: 'message', + toolCalls: [], + }); + }); + + it('rejects an error if message event is not emitted', async () => { + await expect( + streamToResponse(fromEvents(chunkEvent('chunk_1'), tokensEvent())) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"No message event found"`); + }); +}); diff --git a/x-pack/plugins/inference/server/chat_complete/utils/stream_to_response.ts b/x-pack/plugins/inference/server/chat_complete/utils/stream_to_response.ts new file mode 100644 index 0000000000000..4bae4fda767cb --- /dev/null +++ b/x-pack/plugins/inference/server/chat_complete/utils/stream_to_response.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 { toArray, map, firstValueFrom } from 'rxjs'; +import { + ChatCompleteResponse, + ChatCompleteStreamResponse, + createInferenceInternalError, + isChatCompletionMessageEvent, + isChatCompletionTokenCountEvent, + ToolOptions, + withoutChunkEvents, +} from '@kbn/inference-common'; + +export const streamToResponse = ( + streamResponse$: ChatCompleteStreamResponse +): Promise> => { + return firstValueFrom( + streamResponse$.pipe( + withoutChunkEvents(), + toArray(), + map((events) => { + const messageEvent = events.find(isChatCompletionMessageEvent); + const tokenEvent = events.find(isChatCompletionTokenCountEvent); + + if (!messageEvent) { + throw createInferenceInternalError('No message event found'); + } + + return { + content: messageEvent.content, + toolCalls: messageEvent.toolCalls, + tokens: tokenEvent?.tokens, + }; + }) + ) + ); +}; diff --git a/x-pack/plugins/inference/server/index.ts b/x-pack/plugins/inference/server/index.ts index d02dfec733941..60ce870020feb 100644 --- a/x-pack/plugins/inference/server/index.ts +++ b/x-pack/plugins/inference/server/index.ts @@ -4,25 +4,22 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import type { PluginInitializer, PluginInitializerContext } from '@kbn/core/server'; import type { InferenceConfig } from './config'; -import { InferencePlugin } from './plugin'; import type { InferenceServerSetup, InferenceServerStart, InferenceSetupDependencies, InferenceStartDependencies, } from './types'; - -export { withoutTokenCountEvents } from '../common/chat_complete/without_token_count_events'; -export { withoutChunkEvents } from '../common/chat_complete/without_chunk_events'; -export { withoutOutputUpdateEvents } from '../common/output/without_output_update_events'; +import { InferencePlugin } from './plugin'; export type { InferenceClient } from './types'; -export { naturalLanguageToEsql } from './tasks/nl_to_esql'; - export type { InferenceServerSetup, InferenceServerStart }; +export { naturalLanguageToEsql } from './tasks/nl_to_esql'; + export const plugin: PluginInitializer< InferenceServerSetup, InferenceServerStart, diff --git a/x-pack/plugins/inference/server/inference_client/index.ts b/x-pack/plugins/inference/server/inference_client/index.ts index 25208bebc54bb..03da0e3da200f 100644 --- a/x-pack/plugins/inference/server/inference_client/index.ts +++ b/x-pack/plugins/inference/server/inference_client/index.ts @@ -9,7 +9,7 @@ import type { Logger } from '@kbn/logging'; import type { KibanaRequest } from '@kbn/core-http-server'; import type { InferenceClient, InferenceStartDependencies } from '../types'; import { createChatCompleteApi } from '../chat_complete'; -import { createOutputApi } from '../../common/output/create_output_api'; +import { createOutputApi } from '../../common/create_output_api'; import { getConnectorById } from '../util/get_connector_by_id'; export function createInferenceClient({ diff --git a/x-pack/plugins/inference/server/routes/chat_complete.ts b/x-pack/plugins/inference/server/routes/chat_complete.ts index fdf33fbf0af82..582d4ceb97d45 100644 --- a/x-pack/plugins/inference/server/routes/chat_complete.ts +++ b/x-pack/plugins/inference/server/routes/chat_complete.ts @@ -5,11 +5,16 @@ * 2.0. */ -import { schema, Type } from '@kbn/config-schema'; -import type { CoreSetup, IRouter, Logger, RequestHandlerContext } from '@kbn/core/server'; -import { MessageRole } from '../../common/chat_complete'; -import type { ChatCompleteRequestBody } from '../../common/chat_complete/request'; -import { ToolCall, ToolChoiceType } from '../../common/chat_complete/tools'; +import { schema, Type, TypeOf } from '@kbn/config-schema'; +import type { + CoreSetup, + IRouter, + Logger, + RequestHandlerContext, + KibanaRequest, +} from '@kbn/core/server'; +import { MessageRole, ToolCall, ToolChoiceType } from '@kbn/inference-common'; +import type { ChatCompleteRequestBody } from '../../common/http_apis'; import { createInferenceClient } from '../inference_client'; import { InferenceServerStart, InferenceStartDependencies } from '../types'; import { observableIntoEventSourceStream } from '../util/observable_into_event_source_stream'; @@ -85,6 +90,32 @@ export function registerChatCompleteRoute({ router: IRouter; logger: Logger; }) { + async function callChatComplete({ + request, + stream, + }: { + request: KibanaRequest>; + stream: T; + }) { + const actions = await coreSetup + .getStartServices() + .then(([coreStart, pluginsStart]) => pluginsStart.actions); + + const client = createInferenceClient({ request, actions, logger }); + + const { connectorId, messages, system, toolChoice, tools, functionCalling } = request.body; + + return client.chatComplete({ + connectorId, + messages, + system, + toolChoice, + tools, + functionCalling, + stream, + }); + } + router.post( { path: '/internal/inference/chat_complete', @@ -93,23 +124,22 @@ export function registerChatCompleteRoute({ }, }, async (context, request, response) => { - const actions = await coreSetup - .getStartServices() - .then(([coreStart, pluginsStart]) => pluginsStart.actions); - - const client = createInferenceClient({ request, actions, logger }); - - const { connectorId, messages, system, toolChoice, tools, functionCalling } = request.body; - - const chatCompleteResponse = client.chatComplete({ - connectorId, - messages, - system, - toolChoice, - tools, - functionCalling, + const chatCompleteResponse = await callChatComplete({ request, stream: false }); + return response.ok({ + body: chatCompleteResponse, }); + } + ); + router.post( + { + path: '/internal/inference/chat_complete/stream', + validate: { + body: chatCompleteBodySchema, + }, + }, + async (context, request, response) => { + const chatCompleteResponse = await callChatComplete({ request, stream: true }); return response.ok({ body: observableIntoEventSourceStream(chatCompleteResponse, logger), }); diff --git a/x-pack/plugins/inference/server/tasks/nl_to_esql/actions/generate_esql.ts b/x-pack/plugins/inference/server/tasks/nl_to_esql/actions/generate_esql.ts index d31952e2f5252..3d8701eba72db 100644 --- a/x-pack/plugins/inference/server/tasks/nl_to_esql/actions/generate_esql.ts +++ b/x-pack/plugins/inference/server/tasks/nl_to_esql/actions/generate_esql.ts @@ -7,21 +7,23 @@ import { Observable, map, merge, of, switchMap } from 'rxjs'; import type { Logger } from '@kbn/logging'; -import { ToolCall, ToolOptions } from '../../../../common/chat_complete/tools'; import { - correctCommonEsqlMistakes, - generateFakeToolCallId, + ToolCall, + ToolOptions, + withoutTokenCountEvents, isChatCompletionMessageEvent, Message, MessageRole, -} from '../../../../common'; -import { InferenceClient, withoutTokenCountEvents } from '../../..'; -import { OutputCompleteEvent, OutputEventType } from '../../../../common/output'; + OutputCompleteEvent, + OutputEventType, + FunctionCallingMode, +} from '@kbn/inference-common'; +import { correctCommonEsqlMistakes, generateFakeToolCallId } from '../../../../common'; +import { InferenceClient } from '../../..'; import { INLINE_ESQL_QUERY_REGEX } from '../../../../common/tasks/nl_to_esql/constants'; import { EsqlDocumentBase } from '../doc_base'; import { requestDocumentationSchema } from './shared'; import type { NlToEsqlTaskEvent } from '../types'; -import type { FunctionCallingMode } from '../../../../common/chat_complete'; export const generateEsqlTask = ({ chatCompleteApi, @@ -69,6 +71,7 @@ export const generateEsqlTask = ({ chatCompleteApi({ connectorId, functionCalling, + stream: true, system: `${systemMessage} # Current task diff --git a/x-pack/plugins/inference/server/tasks/nl_to_esql/actions/request_documentation.ts b/x-pack/plugins/inference/server/tasks/nl_to_esql/actions/request_documentation.ts index d4eb3060f59bb..06e75db09bdc9 100644 --- a/x-pack/plugins/inference/server/tasks/nl_to_esql/actions/request_documentation.ts +++ b/x-pack/plugins/inference/server/tasks/nl_to_esql/actions/request_documentation.ts @@ -6,11 +6,15 @@ */ import { isEmpty } from 'lodash'; -import { InferenceClient, withoutOutputUpdateEvents } from '../../..'; -import { Message } from '../../../../common'; -import { ToolChoiceType, ToolOptions } from '../../../../common/chat_complete/tools'; +import { + ToolChoiceType, + ToolOptions, + Message, + withoutOutputUpdateEvents, + FunctionCallingMode, +} from '@kbn/inference-common'; +import { InferenceClient } from '../../..'; import { requestDocumentationSchema } from './shared'; -import type { FunctionCallingMode } from '../../../../common/chat_complete'; export const requestDocumentation = ({ outputApi, @@ -29,8 +33,10 @@ export const requestDocumentation = ({ }) => { const hasTools = !isEmpty(tools) && toolChoice !== ToolChoiceType.none; - return outputApi('request_documentation', { + return outputApi({ + id: 'request_documentation', connectorId, + stream: true, functionCalling, system, previousMessages: messages, diff --git a/x-pack/plugins/inference/server/tasks/nl_to_esql/actions/shared.ts b/x-pack/plugins/inference/server/tasks/nl_to_esql/actions/shared.ts index f0fc796173b23..60114188ea37f 100644 --- a/x-pack/plugins/inference/server/tasks/nl_to_esql/actions/shared.ts +++ b/x-pack/plugins/inference/server/tasks/nl_to_esql/actions/shared.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ToolSchema } from '../../../../common'; +import { ToolSchema } from '@kbn/inference-common'; export const requestDocumentationSchema = { type: 'object', diff --git a/x-pack/plugins/inference/server/tasks/nl_to_esql/task.ts b/x-pack/plugins/inference/server/tasks/nl_to_esql/task.ts index e0c5a838ea148..56c48b73f4994 100644 --- a/x-pack/plugins/inference/server/tasks/nl_to_esql/task.ts +++ b/x-pack/plugins/inference/server/tasks/nl_to_esql/task.ts @@ -7,8 +7,7 @@ import { once } from 'lodash'; import { Observable, from, switchMap } from 'rxjs'; -import { Message, MessageRole } from '../../../common/chat_complete'; -import type { ToolOptions } from '../../../common/chat_complete/tools'; +import { Message, MessageRole, ToolOptions } from '@kbn/inference-common'; import { EsqlDocumentBase } from './doc_base'; import { requestDocumentation, generateEsqlTask } from './actions'; import { NlToEsqlTaskParams, NlToEsqlTaskEvent } from './types'; diff --git a/x-pack/plugins/inference/server/tasks/nl_to_esql/types.ts b/x-pack/plugins/inference/server/tasks/nl_to_esql/types.ts index a0bcd635081ea..ce45d9a15e4b3 100644 --- a/x-pack/plugins/inference/server/tasks/nl_to_esql/types.ts +++ b/x-pack/plugins/inference/server/tasks/nl_to_esql/types.ts @@ -11,9 +11,9 @@ import type { ChatCompletionMessageEvent, FunctionCallingMode, Message, -} from '../../../common/chat_complete'; -import type { ToolOptions } from '../../../common/chat_complete/tools'; -import type { OutputCompleteEvent } from '../../../common/output'; + ToolOptions, + OutputCompleteEvent, +} from '@kbn/inference-common'; import type { InferenceClient } from '../../types'; export type NlToEsqlTaskEvent = diff --git a/x-pack/plugins/inference/server/test_utils/chat_complete_events.ts b/x-pack/plugins/inference/server/test_utils/chat_complete_events.ts new file mode 100644 index 0000000000000..4b09ca9c4dc5a --- /dev/null +++ b/x-pack/plugins/inference/server/test_utils/chat_complete_events.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 { + ChatCompletionChunkEvent, + ChatCompletionEventType, + ChatCompletionTokenCountEvent, + ChatCompletionMessageEvent, + ChatCompletionTokenCount, + ToolCall, +} from '@kbn/inference-common'; + +export const chunkEvent = (content: string = 'chunk'): ChatCompletionChunkEvent => ({ + type: ChatCompletionEventType.ChatCompletionChunk, + content, + tool_calls: [], +}); + +export const messageEvent = ( + content: string = 'message', + toolCalls: Array> = [] +): ChatCompletionMessageEvent => ({ + type: ChatCompletionEventType.ChatCompletionMessage, + content, + toolCalls, +}); + +export const tokensEvent = (tokens?: ChatCompletionTokenCount): ChatCompletionTokenCountEvent => ({ + type: ChatCompletionEventType.ChatCompletionTokenCount, + tokens: { + prompt: tokens?.prompt ?? 10, + completion: tokens?.completion ?? 20, + total: tokens?.total ?? 30, + }, +}); diff --git a/x-pack/plugins/inference/server/types.ts b/x-pack/plugins/inference/server/types.ts index 20679ffd4cedf..f538448372e36 100644 --- a/x-pack/plugins/inference/server/types.ts +++ b/x-pack/plugins/inference/server/types.ts @@ -10,9 +10,8 @@ import type { PluginSetupContract as ActionsPluginSetup, } from '@kbn/actions-plugin/server'; import type { KibanaRequest } from '@kbn/core-http-server'; -import { ChatCompleteAPI } from '../common/chat_complete'; +import { ChatCompleteAPI, OutputAPI } from '@kbn/inference-common'; import { InferenceConnector } from '../common/connectors'; -import { OutputAPI } from '../common/output'; /* eslint-disable @typescript-eslint/no-empty-interface*/ diff --git a/x-pack/plugins/inference/server/util/get_connector_by_id.ts b/x-pack/plugins/inference/server/util/get_connector_by_id.ts index 3fd77630ad3d1..1dbf9a6f0d75e 100644 --- a/x-pack/plugins/inference/server/util/get_connector_by_id.ts +++ b/x-pack/plugins/inference/server/util/get_connector_by_id.ts @@ -6,8 +6,8 @@ */ import type { ActionsClient, ActionResult as ActionConnector } from '@kbn/actions-plugin/server'; +import { createInferenceRequestError } from '@kbn/inference-common'; import { isSupportedConnectorType, type InferenceConnector } from '../../common/connectors'; -import { createInferenceRequestError } from '../../common/errors'; /** * Retrieves a connector given the provided `connectorId` and asserts it's an inference connector diff --git a/x-pack/plugins/inference/server/util/observable_into_event_source_stream.test.ts b/x-pack/plugins/inference/server/util/observable_into_event_source_stream.test.ts index ed5466ba1e027..8ece214c27599 100644 --- a/x-pack/plugins/inference/server/util/observable_into_event_source_stream.test.ts +++ b/x-pack/plugins/inference/server/util/observable_into_event_source_stream.test.ts @@ -8,7 +8,7 @@ import { createParser } from 'eventsource-parser'; import { partition } from 'lodash'; import { merge, of, throwError } from 'rxjs'; -import type { InferenceTaskEvent } from '../../common/inference_task'; +import type { InferenceTaskEvent } from '@kbn/inference-common'; import { observableIntoEventSourceStream } from './observable_into_event_source_stream'; import type { Logger } from '@kbn/logging'; diff --git a/x-pack/plugins/inference/server/util/observable_into_event_source_stream.ts b/x-pack/plugins/inference/server/util/observable_into_event_source_stream.ts index bcd1ef60ce1da..62eae6609441f 100644 --- a/x-pack/plugins/inference/server/util/observable_into_event_source_stream.ts +++ b/x-pack/plugins/inference/server/util/observable_into_event_source_stream.ts @@ -9,11 +9,11 @@ import { catchError, map, Observable, of } from 'rxjs'; import { PassThrough } from 'stream'; import type { Logger } from '@kbn/logging'; import { + InferenceTaskEventType, InferenceTaskErrorCode, InferenceTaskErrorEvent, isInferenceError, -} from '../../common/errors'; -import { InferenceTaskEventType } from '../../common/inference_task'; +} from '@kbn/inference-common'; export function observableIntoEventSourceStream( source$: Observable, diff --git a/x-pack/plugins/inference/server/util/validate_tool_calls.test.ts b/x-pack/plugins/inference/server/util/validate_tool_calls.test.ts index 96bf202fa236b..57b030771c6c0 100644 --- a/x-pack/plugins/inference/server/util/validate_tool_calls.test.ts +++ b/x-pack/plugins/inference/server/util/validate_tool_calls.test.ts @@ -5,8 +5,7 @@ * 2.0. */ -import { isToolValidationError } from '../../common/chat_complete/errors'; -import { ToolChoiceType } from '../../common/chat_complete/tools'; +import { ToolChoiceType, isToolValidationError } from '@kbn/inference-common'; import { validateToolCalls } from './validate_tool_calls'; describe('validateToolCalls', () => { diff --git a/x-pack/plugins/inference/server/util/validate_tool_calls.ts b/x-pack/plugins/inference/server/util/validate_tool_calls.ts index 5d1e659bc36f5..ffc2482774b23 100644 --- a/x-pack/plugins/inference/server/util/validate_tool_calls.ts +++ b/x-pack/plugins/inference/server/util/validate_tool_calls.ts @@ -5,16 +5,13 @@ * 2.0. */ import Ajv from 'ajv'; -import { - createToolNotFoundError, - createToolValidationError, -} from '../../common/chat_complete/errors'; import { ToolCallsOf, ToolChoiceType, ToolOptions, UnvalidatedToolCall, -} from '../../common/chat_complete/tools'; +} from '@kbn/inference-common'; +import { createToolNotFoundError, createToolValidationError } from '../chat_complete/errors'; export function validateToolCalls({ toolCalls, diff --git a/x-pack/plugins/inference/tsconfig.json b/x-pack/plugins/inference/tsconfig.json index cc81eec1da96c..92327007829a9 100644 --- a/x-pack/plugins/inference/tsconfig.json +++ b/x-pack/plugins/inference/tsconfig.json @@ -19,7 +19,6 @@ ], "kbn_references": [ "@kbn/i18n", - "@kbn/sse-utils", "@kbn/esql-ast", "@kbn/esql-validation-autocomplete", "@kbn/core", @@ -33,6 +32,7 @@ "@kbn/core-http-server", "@kbn/actions-plugin", "@kbn/config-schema", + "@kbn/inference-common", "@kbn/es-types", "@kbn/field-types", "@kbn/expressions-plugin", diff --git a/x-pack/plugins/integration_assistant/docs/imgs/categorization_graph.png b/x-pack/plugins/integration_assistant/docs/imgs/categorization_graph.png index a15dbf54d905a..de45a18546b40 100644 Binary files a/x-pack/plugins/integration_assistant/docs/imgs/categorization_graph.png and b/x-pack/plugins/integration_assistant/docs/imgs/categorization_graph.png differ diff --git a/x-pack/plugins/integration_assistant/docs/imgs/cel_graph.png b/x-pack/plugins/integration_assistant/docs/imgs/cel_graph.png new file mode 100644 index 0000000000000..8b339caa83798 Binary files /dev/null and b/x-pack/plugins/integration_assistant/docs/imgs/cel_graph.png differ diff --git a/x-pack/plugins/integration_assistant/docs/imgs/log_detection_graph.png b/x-pack/plugins/integration_assistant/docs/imgs/log_detection_graph.png index 285e012c57a14..f89ffc800f0a5 100644 Binary files a/x-pack/plugins/integration_assistant/docs/imgs/log_detection_graph.png and b/x-pack/plugins/integration_assistant/docs/imgs/log_detection_graph.png differ diff --git a/x-pack/plugins/integration_assistant/docs/imgs/related_graph.png b/x-pack/plugins/integration_assistant/docs/imgs/related_graph.png index 73a2c3acac0d4..222c864f3115f 100644 Binary files a/x-pack/plugins/integration_assistant/docs/imgs/related_graph.png and b/x-pack/plugins/integration_assistant/docs/imgs/related_graph.png differ diff --git a/x-pack/plugins/integration_assistant/docs/imgs/unstructured_graph.png b/x-pack/plugins/integration_assistant/docs/imgs/unstructured_graph.png new file mode 100644 index 0000000000000..45ab5abc8d6b3 Binary files /dev/null and b/x-pack/plugins/integration_assistant/docs/imgs/unstructured_graph.png differ diff --git a/x-pack/plugins/integration_assistant/scripts/draw_graphs_script.ts b/x-pack/plugins/integration_assistant/scripts/draw_graphs_script.ts index 12a37f71b184a..f9fd71c1f934f 100644 --- a/x-pack/plugins/integration_assistant/scripts/draw_graphs_script.ts +++ b/x-pack/plugins/integration_assistant/scripts/draw_graphs_script.ts @@ -20,6 +20,8 @@ import { getEcsGraph, getEcsSubGraph } from '../server/graphs/ecs/graph'; import { getLogFormatDetectionGraph } from '../server/graphs/log_type_detection/graph'; import { getRelatedGraph } from '../server/graphs/related/graph'; import { getKVGraph } from '../server/graphs/kv/graph'; +import { getUnstructuredGraph } from '../server/graphs/unstructured'; +import { getCelGraph } from '../server/graphs/cel/graph'; // Some mock elements just to get the graph to compile const model = new FakeLLM({ @@ -45,17 +47,20 @@ async function drawGraph(compiledGraph: RunnableGraph, graphName: string) { await saveFile(`${graphName}.png`, buffer); } +const GRAPH_LIST = { + related_graph: getRelatedGraph, + log_detection_graph: getLogFormatDetectionGraph, + categorization_graph: getCategorizationGraph, + kv_graph: getKVGraph, + ecs_graph: getEcsGraph, + ecs_subgraph: getEcsSubGraph, + unstructured_graph: getUnstructuredGraph, + cel_graph: getCelGraph, +}; + export async function drawGraphs() { - const relatedGraph = (await getRelatedGraph({ client, model })).getGraph(); - const logFormatDetectionGraph = (await getLogFormatDetectionGraph({ client, model })).getGraph(); - const categorizationGraph = (await getCategorizationGraph({ client, model })).getGraph(); - const ecsSubGraph = (await getEcsSubGraph({ model })).getGraph(); - const ecsGraph = (await getEcsGraph({ model })).getGraph(); - const kvGraph = (await getKVGraph({ client, model })).getGraph(); - drawGraph(relatedGraph, 'related_graph'); - drawGraph(logFormatDetectionGraph, 'log_detection_graph'); - drawGraph(categorizationGraph, 'categorization_graph'); - drawGraph(ecsSubGraph, 'ecs_subgraph'); - drawGraph(ecsGraph, 'ecs_graph'); - drawGraph(kvGraph, 'kv_graph'); + for (const [name, graph] of Object.entries(GRAPH_LIST)) { + const compiledGraph = (await graph({ client, model })).getGraph(); + drawGraph(compiledGraph, name); + } } diff --git a/x-pack/plugins/integration_assistant/server/graphs/categorization/graph.ts b/x-pack/plugins/integration_assistant/server/graphs/categorization/graph.ts index 2f07bcd106862..cc1601095da62 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/categorization/graph.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/categorization/graph.ts @@ -233,6 +233,6 @@ export async function getCategorizationGraph({ client, model }: CategorizationGr } ); - const compiledCategorizationGraph = workflow.compile().withConfig({ runName: 'Categorization' }); + const compiledCategorizationGraph = workflow.compile(); return compiledCategorizationGraph; } diff --git a/x-pack/plugins/integration_assistant/server/graphs/cel/graph.ts b/x-pack/plugins/integration_assistant/server/graphs/cel/graph.ts index a8f2e0521c788..5d58f82f6f744 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/cel/graph.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/cel/graph.ts @@ -104,6 +104,6 @@ export async function getCelGraph({ model }: CelInputGraphParams) { .addEdge('handleGetStateVariables', 'handleGetStateDetails') .addEdge('handleGetStateDetails', 'modelOutput'); - const compiledCelGraph = workflow.compile().withConfig({ runName: 'CEL' }); + const compiledCelGraph = workflow.compile(); return compiledCelGraph; } diff --git a/x-pack/plugins/integration_assistant/server/graphs/ecs/graph.ts b/x-pack/plugins/integration_assistant/server/graphs/ecs/graph.ts index 89a7e5c600723..dc2f26f9505e4 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/ecs/graph.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/ecs/graph.ts @@ -78,7 +78,7 @@ export async function getEcsSubGraph({ model }: EcsGraphParams) { }) .addEdge('modelSubOutput', END); - const compiledEcsSubGraph = workflow.compile().withConfig({ runName: 'ECS Mapping (Chunk)' }); + const compiledEcsSubGraph = workflow.compile(); return compiledEcsSubGraph; } @@ -96,7 +96,7 @@ export async function getEcsGraph({ model }: EcsGraphParams) { .addNode('handleMergedSubGraphResponse', (state: EcsMappingState) => modelMergedInputFromSubGraph({ state }) ) - .addNode('subGraph', subGraph) + .addNode('subGraph', subGraph.withConfig({ runName: 'ECS Mapping (Chunk)' })) .addEdge(START, 'modelInput') .addEdge('subGraph', 'handleMergedSubGraphResponse') .addEdge('handleDuplicates', 'handleValidation') @@ -119,6 +119,6 @@ export async function getEcsGraph({ model }: EcsGraphParams) { }) .addEdge('modelOutput', END); - const compiledEcsGraph = workflow.compile().withConfig({ runName: 'ECS Mapping' }); + const compiledEcsGraph = workflow.compile(); return compiledEcsGraph; } diff --git a/x-pack/plugins/integration_assistant/server/graphs/kv/graph.ts b/x-pack/plugins/integration_assistant/server/graphs/kv/graph.ts index f72984655c1f8..6f7b43ba40f22 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/kv/graph.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/kv/graph.ts @@ -139,6 +139,6 @@ export async function getKVGraph({ model, client }: KVGraphParams) { }) .addEdge('modelOutput', END); - const compiledKVGraph = workflow.compile().withConfig({ runName: 'Key-Value' }); + const compiledKVGraph = workflow.compile(); return compiledKVGraph; } diff --git a/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/graph.ts b/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/graph.ts index 95d624a7436c7..ae4c607ab3f68 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/graph.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/graph.ts @@ -118,8 +118,14 @@ export async function getLogFormatDetectionGraph({ model, client }: LogDetection .addNode('handleLogFormatDetection', (state: LogFormatDetectionState) => handleLogFormatDetection({ state, model, client }) ) - .addNode('handleKVGraph', await getKVGraph({ model, client })) - .addNode('handleUnstructuredGraph', await getUnstructuredGraph({ model, client })) + .addNode( + 'handleKVGraph', + (await getKVGraph({ model, client })).withConfig({ runName: 'Key-Value' }) + ) + .addNode( + 'handleUnstructuredGraph', + (await getUnstructuredGraph({ model, client })).withConfig({ runName: 'Unstructured' }) + ) .addNode('handleCSV', (state: LogFormatDetectionState) => handleCSV({ state, model, client })) .addEdge(START, 'modelInput') .addEdge('modelInput', 'handleLogFormatDetection') @@ -138,6 +144,6 @@ export async function getLogFormatDetectionGraph({ model, client }: LogDetection } ); - const compiledLogFormatDetectionGraph = workflow.compile().withConfig({ runName: 'Log Format' }); + const compiledLogFormatDetectionGraph = workflow.compile(); return compiledLogFormatDetectionGraph; } diff --git a/x-pack/plugins/integration_assistant/server/graphs/related/graph.ts b/x-pack/plugins/integration_assistant/server/graphs/related/graph.ts index e8dc44a152e80..20ac1c639dcf4 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/related/graph.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/related/graph.ts @@ -179,6 +179,6 @@ export async function getRelatedGraph({ client, model }: RelatedGraphParams) { } ); - const compiledRelatedGraph = workflow.compile().withConfig({ runName: 'Related' }); + const compiledRelatedGraph = workflow.compile(); return compiledRelatedGraph; } diff --git a/x-pack/plugins/integration_assistant/server/graphs/unstructured/graph.ts b/x-pack/plugins/integration_assistant/server/graphs/unstructured/graph.ts index cf3a645effa68..6048404728bfb 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/unstructured/graph.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/unstructured/graph.ts @@ -107,6 +107,6 @@ export async function getUnstructuredGraph({ model, client }: UnstructuredGraphP .addEdge('handleUnstructuredError', 'handleUnstructuredValidate') .addEdge('modelOutput', END); - const compiledUnstructuredGraph = workflow.compile().withConfig({ runName: 'Unstructured' }); + const compiledUnstructuredGraph = workflow.compile(); return compiledUnstructuredGraph; } diff --git a/x-pack/plugins/integration_assistant/server/routes/analyze_logs_routes.ts b/x-pack/plugins/integration_assistant/server/routes/analyze_logs_routes.ts index 639cd62f275b1..34f05fcc82025 100644 --- a/x-pack/plugins/integration_assistant/server/routes/analyze_logs_routes.ts +++ b/x-pack/plugins/integration_assistant/server/routes/analyze_logs_routes.ts @@ -36,6 +36,13 @@ export function registerAnalyzeLogsRoutes( .addVersion( { version: '1', + security: { + authz: { + enabled: false, + reason: + 'This route is opted out from authorization because the privileges are not defined yet.', + }, + }, validate: { request: { body: buildRouteValidationWithZod(AnalyzeLogsRequestBody), @@ -91,7 +98,9 @@ export function registerAnalyzeLogsRoutes( logSamples, }; const graph = await getLogFormatDetectionGraph({ model, client }); - const graphResults = await graph.invoke(logFormatParameters, options); + const graphResults = await graph + .withConfig({ runName: 'Log Format' }) + .invoke(logFormatParameters, options); const graphLogFormat = graphResults.results.samplesFormat.name; if (graphLogFormat === 'unsupported') { throw new UnsupportedLogFormatError(GenerationErrorCode.UNSUPPORTED_LOG_SAMPLES_FORMAT); diff --git a/x-pack/plugins/integration_assistant/server/routes/build_integration_routes.ts b/x-pack/plugins/integration_assistant/server/routes/build_integration_routes.ts index 6d7e5155a3d23..f62d6d55f933d 100644 --- a/x-pack/plugins/integration_assistant/server/routes/build_integration_routes.ts +++ b/x-pack/plugins/integration_assistant/server/routes/build_integration_routes.ts @@ -25,6 +25,13 @@ export function registerIntegrationBuilderRoutes( .addVersion( { version: '1', + security: { + authz: { + enabled: false, + reason: + 'This route is opted out from authorization because the privileges are not defined yet.', + }, + }, validate: { request: { body: buildRouteValidationWithZod(BuildIntegrationRequestBody), diff --git a/x-pack/plugins/integration_assistant/server/routes/categorization_routes.test.ts b/x-pack/plugins/integration_assistant/server/routes/categorization_routes.test.ts index abe626cf7ae73..0e6f4ffa0491a 100644 --- a/x-pack/plugins/integration_assistant/server/routes/categorization_routes.test.ts +++ b/x-pack/plugins/integration_assistant/server/routes/categorization_routes.test.ts @@ -23,7 +23,9 @@ const mockResult = jest.fn().mockResolvedValue({ jest.mock('../graphs/categorization', () => { return { getCategorizationGraph: jest.fn().mockResolvedValue({ - invoke: () => mockResult(), + withConfig: () => ({ + invoke: () => mockResult(), + }), }), }; }); diff --git a/x-pack/plugins/integration_assistant/server/routes/categorization_routes.ts b/x-pack/plugins/integration_assistant/server/routes/categorization_routes.ts index 77ce549f589f4..5f63ed9c7bf3c 100644 --- a/x-pack/plugins/integration_assistant/server/routes/categorization_routes.ts +++ b/x-pack/plugins/integration_assistant/server/routes/categorization_routes.ts @@ -40,6 +40,13 @@ export function registerCategorizationRoutes( .addVersion( { version: '1', + security: { + authz: { + enabled: false, + reason: + 'This route is opted out from authorization because the privileges are not defined yet.', + }, + }, validate: { request: { body: buildRouteValidationWithZod(CategorizationRequestBody), @@ -99,7 +106,9 @@ export function registerCategorizationRoutes( }; const graph = await getCategorizationGraph({ client, model }); - const results = await graph.invoke(parameters, options); + const results = await graph + .withConfig({ runName: 'Categorization' }) + .invoke(parameters, options); return res.ok({ body: CategorizationResponse.parse(results) }); } catch (err) { diff --git a/x-pack/plugins/integration_assistant/server/routes/cel_route.test.ts b/x-pack/plugins/integration_assistant/server/routes/cel_route.test.ts index be435aa9866bb..02b5f03948a12 100644 --- a/x-pack/plugins/integration_assistant/server/routes/cel_route.test.ts +++ b/x-pack/plugins/integration_assistant/server/routes/cel_route.test.ts @@ -22,7 +22,9 @@ const mockResult = jest.fn().mockResolvedValue({ jest.mock('../graphs/cel', () => { return { getCelGraph: jest.fn().mockResolvedValue({ - invoke: () => mockResult(), + withConfig: () => ({ + invoke: () => mockResult(), + }), }), }; }); diff --git a/x-pack/plugins/integration_assistant/server/routes/cel_routes.ts b/x-pack/plugins/integration_assistant/server/routes/cel_routes.ts index ecf012a88cfe5..9ce16c3909119 100644 --- a/x-pack/plugins/integration_assistant/server/routes/cel_routes.ts +++ b/x-pack/plugins/integration_assistant/server/routes/cel_routes.ts @@ -32,6 +32,13 @@ export function registerCelInputRoutes(router: IRouter { return { getEcsGraph: jest.fn().mockResolvedValue({ - invoke: () => mockResult(), + withConfig: () => ({ + invoke: () => mockResult(), + }), }), }; }); diff --git a/x-pack/plugins/integration_assistant/server/routes/ecs_routes.ts b/x-pack/plugins/integration_assistant/server/routes/ecs_routes.ts index 43ca0fe396cae..adb30d6c03fba 100644 --- a/x-pack/plugins/integration_assistant/server/routes/ecs_routes.ts +++ b/x-pack/plugins/integration_assistant/server/routes/ecs_routes.ts @@ -34,6 +34,13 @@ export function registerEcsRoutes(router: IRouter { return { getRelatedGraph: jest.fn().mockResolvedValue({ - invoke: () => mockResult(), + withConfig: () => ({ + invoke: () => mockResult(), + }), }), }; }); diff --git a/x-pack/plugins/integration_assistant/server/routes/related_routes.ts b/x-pack/plugins/integration_assistant/server/routes/related_routes.ts index fe3a63abd4ce9..d839ce2a08620 100644 --- a/x-pack/plugins/integration_assistant/server/routes/related_routes.ts +++ b/x-pack/plugins/integration_assistant/server/routes/related_routes.ts @@ -34,6 +34,13 @@ export function registerRelatedRoutes(router: IRouter { + if (e.key === keys.ESCAPE) { + closeFlyout?.(); + setIsInlineFlyoutVisible(false); + } + }; + if (isLoading) return null; // Example is the Discover editing where we dont want to render the text based editor on the panel, neither the suggestions (for now) if (!canEditTextBasedQuery && hidesSuggestions) { return ( - - - + <> + {isInlineFlyoutVisible && } + + + + ); } return ( <> + {isInlineFlyoutVisible && } { expect((suggestions[0].state.layers[0] as XYDataLayerConfig).seriesType).toEqual('line'); }); - test('suggests line if changeType is initial and date column is involved', () => { + test('suggests bar if changeType is initial and date column is involved', () => { const currentState: XYState = { legend: { isVisible: true, position: 'bottom' }, valueLabels: 'hide', @@ -885,8 +885,8 @@ describe('xy_suggestions', () => { expect(suggestions).toHaveLength(1); expect(suggestions[0].hide).toEqual(false); - expect(suggestions[0].state.preferredSeriesType).toEqual('line'); - expect((suggestions[0].state.layers[0] as XYDataLayerConfig).seriesType).toEqual('line'); + expect(suggestions[0].state.preferredSeriesType).toEqual('bar_stacked'); + expect((suggestions[0].state.layers[0] as XYDataLayerConfig).seriesType).toEqual('bar_stacked'); }); test('makes a visible seriesType suggestion for unchanged table without split', () => { diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts index 5efaf4d8c949e..f13bcc57e3c84 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts @@ -233,9 +233,6 @@ function getSuggestionsForLayer({ allowMixed, }; - if (changeType === 'initial' && xValue?.operation.dataType === 'date') { - return buildSuggestion({ ...options, seriesType: 'line' }); - } // handles the simplest cases, acting as a chart switcher if (!currentState && changeType === 'unchanged') { // Chart switcher needs to include every chart type diff --git a/x-pack/plugins/ml/public/application/components/job_selector/use_job_selection.ts b/x-pack/plugins/ml/public/application/components/job_selector/use_job_selection.ts index 7fcc1e71e1808..51d1882084d3e 100644 --- a/x-pack/plugins/ml/public/application/components/job_selector/use_job_selection.ts +++ b/x-pack/plugins/ml/public/application/components/job_selector/use_job_selection.ts @@ -25,6 +25,24 @@ function getInvalidJobIds(jobs: MlJobWithTimeRange[], ids: string[]) { }); } +// This is useful when redirecting from dashboards where groupIds are treated as jobIds +const getJobIdsFromGroups = (jobIds: string[], jobs: MlJobWithTimeRange[]) => { + const result = new Set(); + + jobIds.forEach((id) => { + const jobsInGroup = jobs.filter((job) => job.groups?.includes(id)); + + if (jobsInGroup.length > 0) { + jobsInGroup.forEach((job) => result.add(job.job_id)); + } else { + // If it's not a group ID, keep it (regardless of whether it's valid or not) + result.add(id); + } + }); + + return Array.from(result); +}; + export interface JobSelection { jobIds: string[]; selectedGroups: string[]; @@ -37,9 +55,9 @@ export const useJobSelection = (jobs: MlJobWithTimeRange[]) => { const getJobSelection = useJobSelectionFlyout(); const tmpIds = useMemo(() => { - const ids = globalState?.ml?.jobIds || []; + const ids = getJobIdsFromGroups(globalState?.ml?.jobIds || [], jobs); return (typeof ids === 'string' ? [ids] : ids).map((id: string) => String(id)); - }, [globalState?.ml?.jobIds]); + }, [globalState?.ml?.jobIds, jobs]); const invalidIds = useMemo(() => { return getInvalidJobIds(jobs, tmpIds); 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 3212eba8b2ddd..7fd678e98f6fd 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 @@ -576,6 +576,7 @@ export const ConfigurationStepForm: FC = ({ fieldStatsServices={fieldStatsServices} timeRangeMs={indexData.timeRangeMs} dslQuery={jobConfigQuery} + theme={services.theme} > diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/_time_range_selector.scss b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/_time_range_selector.scss deleted file mode 100644 index faa69e90ecab5..0000000000000 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/_time_range_selector.scss +++ /dev/null @@ -1,61 +0,0 @@ -// stylelint-disable selector-no-qualifying-type -// SASSTODO: Looks like this could use a rewrite. Needs selectors -.time-range-selector { - .time-range-section-title { - font-weight: bold; - margin-bottom: $euiSizeS; - } - .time-range-section { - flex: 50%; - padding: 0 $euiSizeS; - border-right: $euiBorderThin; - } - - .tab-stack { - margin-bottom: 0; - padding-left: 0; - list-style: none; - - & > li { - float: none; - position: relative; - display: block; - margin-bottom: $euiSizeXS; - - & > a { - position: relative; - display: block; - padding: $euiSizeS $euiSize; - border-radius: $euiSizeXS; - } - & > a:hover { - background-color: $euiColorLightestShade; - } - .body { - display: none; - } - } - & > li.active { - & > a { - color: $euiColorEmptyShade; - background-color: $euiColorPrimary; - - } - .body { - display: block; - } - } - & > li.has-body.active { - & > a { - border-radius: $euiBorderRadius $euiBorderRadius 0 0; - } - .react-datepicker { - border-radius: 0 0 $euiBorderRadius $euiBorderRadius; - border-top: none; - } - } - } - .time-range-section:last-child { - border-right: none; - } -} diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/time_range_selector.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/time_range_selector.js index af3a4d22c1e7e..a6889c745f763 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/time_range_selector.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/time_range_selector.js @@ -5,7 +5,6 @@ * 2.0. */ -import './_time_range_selector.scss'; import PropTypes from 'prop-types'; import React, { Component, useState, useEffect } from 'react'; @@ -16,6 +15,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { TIME_FORMAT } from '@kbn/ml-date-utils'; import { ManagedJobsWarningCallout } from '../../confirm_modals/managed_jobs_warning_callout'; +import { TimeRangeSelectorWrapper } from './time_range_selector_wrapper'; export class TimeRangeSelector extends Component { constructor(props) { @@ -166,7 +166,7 @@ export class TimeRangeSelector extends Component { render() { const { startItems, endItems } = this.getTabItems(); return ( -
+ {this.props.hasManagedJob === true && this.state.endTab !== 0 ? ( <> -
+ ); } } diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/time_range_selector_wrapper.tsx b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/time_range_selector_wrapper.tsx new file mode 100644 index 0000000000000..fed58a975eabb --- /dev/null +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/time_range_selector_wrapper.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 type { FC, PropsWithChildren } from 'react'; +import React from 'react'; +import { useEuiTheme } from '@elastic/eui'; + +export const TimeRangeSelectorWrapper: FC = ({ children }) => { + const { euiTheme } = useEuiTheme(); + const style = { + '.time-range-section-title': { + fontWeight: 'bold', + marginBottom: euiTheme.size.s, + }, + '.time-range-section': { + flex: '50%', + padding: `0 ${euiTheme.size.s}`, + borderRight: euiTheme.border.thin, + }, + + '.tab-stack': { + marginBottom: 0, + paddingLeft: 0, + listStyle: 'none', + + '& > li': { + float: 'none', + position: 'relative', + display: 'block', + marginBottom: euiTheme.size.xs, + + '& > a': { + position: 'relative', + display: 'block', + padding: `${euiTheme.size.s} ${euiTheme.size.base}`, + borderRadius: euiTheme.border.radius.medium, + }, + '& > a:hover': { + backgroundColor: euiTheme.colors.lightestShade, + }, + '.body': { + display: 'none', + }, + }, + '& > li.active': { + '& > a': { + color: euiTheme.colors.emptyShade, + backgroundColor: euiTheme.colors.primary, + }, + '.body': { + display: 'block', + '.euiFieldText': { + borderRadius: `0 0 ${euiTheme.border.radius.medium} ${euiTheme.border.radius.medium}`, + }, + }, + }, + '& > li.has-body.active': { + '& > a': { + borderRadius: `${euiTheme.border.radius.medium} ${euiTheme.border.radius.medium} 0 0`, + }, + '.react-datepicker': { + borderRadius: `0 0 ${euiTheme.border.radius.medium} ${euiTheme.border.radius.medium}`, + borderTop: 'none', + }, + }, + }, + '.time-range-section:last-child': { + borderRight: 'none', + }, + }; + + // @ts-expect-error style object strings cause a type error + return
{children}
; +}; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/split_cards/split_cards.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/split_cards/split_cards.tsx index 7966a73c85faa..d09791941a379 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/split_cards/split_cards.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/split_cards/split_cards.tsx @@ -8,10 +8,16 @@ import type { FC, PropsWithChildren } from 'react'; import React, { memo, Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiHorizontalRule, EuiSpacer } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiHorizontalRule, + EuiSpacer, + useEuiTheme, +} from '@elastic/eui'; import type { SplitField } from '@kbn/ml-anomaly-utils'; import { JOB_TYPE } from '../../../../../../../../../common/constants/new_job'; -import './style.scss'; interface Props { fieldValues: string[]; @@ -28,8 +34,14 @@ interface Panel { export const SplitCards: FC> = memo( ({ fieldValues, splitField, children, numberOfDetectors, jobType, animate = false }) => { + const { euiTheme } = useEuiTheme(); const panels: Panel[] = []; + const splitCardStyle = { + border: euiTheme.border.thin, + paddingTop: euiTheme.size.xs, + }; + function storePanels(panel: HTMLDivElement | null, marginBottom: number) { if (panel !== null) { if (animate === false) { @@ -70,14 +82,10 @@ export const SplitCards: FC> = memo( ...(animate ? { transition: 'margin 0.5s' } : {}), }; return ( -
storePanels(ref, marginBottom)} style={style}> - +
storePanels(ref, marginBottom)} css={style}> +
{fieldName} @@ -97,7 +105,7 @@ export const SplitCards: FC> = memo( {(jobType === JOB_TYPE.MULTI_METRIC || jobType === JOB_TYPE.GEO) && (
> = memo( )} {getBackPanels()} - +
{fieldValues[0]} diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/split_cards/style.scss b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/split_cards/style.scss deleted file mode 100644 index b6b4be7ab5c9d..0000000000000 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/split_cards/style.scss +++ /dev/null @@ -1,4 +0,0 @@ -.mlPickFields__splitCard { - padding-top: $euiSizeXS; - border: $euiBorderThin; -} diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/wizard_steps.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/wizard_steps.tsx index 6c4600be5d25e..b44c523bc57cf 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/wizard_steps.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/wizard_steps.tsx @@ -123,6 +123,7 @@ export const WizardSteps: FC = ({ currentStep, setCurrentStep }) => { fieldStatsServices={fieldStatsServices} timeRangeMs={timeRangeMs} dslQuery={jobCreator.query} + theme={services.theme} > <> 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 f218030c65ad3..a717995d4ee14 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 @@ -316,6 +316,16 @@ export const ModelsList: FC = ({ }; }); }); + + setItemIdToExpandedRowMap((prev) => { + // Refresh expanded rows + return Object.fromEntries( + Object.keys(prev).map((modelId) => { + const item = resultItems.find((i) => i.model_id === modelId); + return item ? [modelId, ] : []; + }) + ); + }); } catch (error) { displayErrorToast( error, @@ -947,6 +957,14 @@ export const ModelsList: FC = ({ } }); + setItemIdToExpandedRowMap((prev) => { + const newMap = { ...prev }; + modelsToDelete.forEach((model) => { + delete newMap[model.model_id]; + }); + return newMap; + }); + setModelsToDelete([]); if (refreshList) { diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts index f69e60453bfd4..3552b8f006091 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts @@ -11,7 +11,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { RuntimeMappings } from '@kbn/ml-runtime-field-utils'; -import { isNumber } from 'lodash'; +import { chunk, isNumber } from 'lodash'; import { ML_INTERNAL_BASE_PATH } from '../../../../common/constants/app'; import type { MlServerDefaults, @@ -27,7 +27,6 @@ import type { JobStats, Datafeed, CombinedJob, - Detector, AnalysisConfig, ModelSnapshot, IndicesOptions, @@ -350,15 +349,6 @@ export function mlApiProvider(httpService: HttpService) { }); }, - validateDetector({ detector }: { detector: Detector }) { - const body = JSON.stringify(detector); - return httpService.http({ - path: `${ML_INTERNAL_BASE_PATH}/anomaly_detectors/_validate/detector`, - method: 'POST', - body, - }); - }, - forecast({ jobId, duration, @@ -397,13 +387,13 @@ export function mlApiProvider(httpService: HttpService) { end, overallScore, }: { - jobId: string; + jobId: string[]; topN: string; bucketSpan: string; start: number; end: number; overallScore?: number; - }) { + }): Promise { const body = JSON.stringify({ topN, bucketSpan, @@ -411,11 +401,31 @@ export function mlApiProvider(httpService: HttpService) { end, ...(overallScore ? { overall_score: overallScore } : {}), }); - return httpService.http({ - path: `${ML_INTERNAL_BASE_PATH}/anomaly_detectors/${jobId}/results/overall_buckets`, - method: 'POST', - body, - version: '1', + + // Max permitted job_id is 64 characters, so we can fit around 30 jobs per request + const maxJobsPerRequest = 30; + + return Promise.all( + chunk(jobId, maxJobsPerRequest).map((jobIdsChunk) => { + return httpService.http({ + path: `${ML_INTERNAL_BASE_PATH}/anomaly_detectors/${jobIdsChunk.join( + ',' + )}/results/overall_buckets`, + method: 'POST', + body, + version: '1', + }); + }) + ).then((responses) => { + // Merge responses + return responses.reduce( + (acc, response) => { + acc.count += response.count; + acc.overall_buckets.push(...response.overall_buckets); + return acc; + }, + { count: 0, overall_buckets: [] } + ); }); }, diff --git a/x-pack/plugins/ml/server/lib/ml_client/types.ts b/x-pack/plugins/ml/server/lib/ml_client/types.ts index 93977257cdc22..ca7b36df8f208 100644 --- a/x-pack/plugins/ml/server/lib/ml_client/types.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/types.ts @@ -121,8 +121,7 @@ export type MlClientParams = | Parameters | Parameters | Parameters - | Parameters - | Parameters; + | Parameters; export type MlGetADParams = Parameters | Parameters; diff --git a/x-pack/plugins/ml/server/routes/anomaly_detectors.ts b/x-pack/plugins/ml/server/routes/anomaly_detectors.ts index 8cd9f45a4217e..4c75b7a85556a 100644 --- a/x-pack/plugins/ml/server/routes/anomaly_detectors.ts +++ b/x-pack/plugins/ml/server/routes/anomaly_detectors.ts @@ -349,37 +349,6 @@ export function jobRoutes({ router, routeGuard }: RouteInitialization) { }) ); - router.versioned - .post({ - path: `${ML_INTERNAL_BASE_PATH}/anomaly_detectors/_validate/detector`, - access: 'internal', - options: { - tags: ['access:ml:canCreateJob'], - }, - summary: 'Validates detector', - description: 'Validates specified detector.', - }) - .addVersion( - { - version: '1', - validate: { - request: { - body: schema.any(), - }, - }, - }, - routeGuard.fullLicenseAPIGuard(async ({ mlClient, request, response }) => { - try { - const body = await mlClient.validateDetector({ body: request.body }); - return response.ok({ - body, - }); - } catch (e) { - return response.customError(wrapError(e)); - } - }) - ); - router.versioned .delete({ path: `${ML_INTERNAL_BASE_PATH}/anomaly_detectors/{jobId}/_forecast/{forecastId}`, diff --git a/x-pack/plugins/ml/server/routes/system.ts b/x-pack/plugins/ml/server/routes/system.ts index bf4fa3161f5b9..b6765c4b5f16c 100644 --- a/x-pack/plugins/ml/server/routes/system.ts +++ b/x-pack/plugins/ml/server/routes/system.ts @@ -97,6 +97,13 @@ export function systemRoutes( .addVersion( { version: '1', + security: { + authz: { + enabled: false, + reason: + 'This route is opted out from authorization because permissions will be checked by elasticsearch', + }, + }, validate: false, }, routeGuard.basicLicenseAPIGuard(async ({ mlClient, request, response }) => { diff --git a/x-pack/plugins/observability_solution/apm/common/entities/types.ts b/x-pack/plugins/observability_solution/apm/common/entities/types.ts deleted file mode 100644 index 9775b1e32eae6..0000000000000 --- a/x-pack/plugins/observability_solution/apm/common/entities/types.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 enum EntityDataStreamType { - METRICS = 'metrics', - TRACES = 'traces', - LOGS = 'logs', -} diff --git a/x-pack/plugins/observability_solution/apm/common/es_fields/entities.ts b/x-pack/plugins/observability_solution/apm/common/es_fields/entities.ts deleted file mode 100644 index 28e4a3ec79165..0000000000000 --- a/x-pack/plugins/observability_solution/apm/common/es_fields/entities.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 const ENTITY_METRICS_LATENCY = 'entity.metrics.latency'; -export const ENTITY_METRICS_LOG_ERROR_RATE = 'entity.metrics.logErrorRate'; -export const ENTITY_METRICS_LOG_RATE = 'entity.metrics.logRate'; -export const ENTITY_METRICS_THROUGHPUT = 'entity.metrics.throughput'; -export const ENTITY_METRICS_FAILED_TRANSACTION_RATE = 'entity.metrics.failedTransactionRate'; diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm.yaml deleted file mode 100644 index d37137302fd21..0000000000000 --- a/x-pack/plugins/observability_solution/apm/docs/openapi/apm.yaml +++ /dev/null @@ -1,186 +0,0 @@ -openapi: 3.0.0 -info: - title: APM UI - version: 1.0.0 -tags: - - name: APM agent keys - description: > - Configure APM agent keys to authorize requests from APM agents to the APM Server. - - name: APM annotations - description: > - Annotate visualizations in the APM app with significant events. - Annotations enable you to easily see how events are impacting the performance of your applications. -paths: - /api/apm/agent_keys: - post: - summary: Create an APM agent key - description: Create a new agent key for APM. - operationId: createAgentKey - tags: - - APM agent keys - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - name: - type: string - privileges: - type: array - items: - type: string - enum: - - event:write - - config_agent:read - responses: - "200": - description: Agent key created successfully - content: - application/json: - schema: - type: object - properties: - api_key: - type: string - expiration: - type: integer - format: int64 - id: - type: string - name: - type: string - encoded: - type: string - /api/apm/services/{serviceName}/annotation/search: - get: - summary: Search for annotations - description: Search for annotations related to a specific service. - operationId: getAnnotation - tags: - - APM annotations - parameters: - - name: serviceName - in: path - required: true - description: The name of the service - schema: - type: string - - name: environment - in: query - required: false - description: The environment to filter annotations by - schema: - type: string - - name: start - in: query - required: false - description: The start date for the search - schema: - type: string - - name: end - in: query - required: false - description: The end date for the search - schema: - type: string - responses: - "200": - description: Successful response - content: - application/json: - schema: - type: object - properties: - annotations: - type: array - items: - type: object - properties: - type: - type: string - enum: - - version - id: - type: string - "@timestamp": - type: number - text: - type: string - /api/apm/services/{serviceName}/annotation: - post: - summary: Create a service annotation - description: Create a new annotation for a specific service. - operationId: createAnnotation - tags: - - APM annotations - parameters: - - name: serviceName - in: path - required: true - description: The name of the service - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - '@timestamp': - type: string - service: - type: object - properties: - version: - type: string - environment: - type: string - message: - type: string - tags: - type: array - items: - type: string - - responses: - '200': - description: Annotation created successfully - content: - application/json: - schema: - type: object - properties: - _id: - type: string - _index: - type: string - _source: - type: object - properties: - annotation: - type: string - tags: - type: array - items: - type: string - message: - type: string - service: - type: object - properties: - name: - type: string - environment: - type: string - version: - type: string - event: - type: object - properties: - created: - type: string - '@timestamp': - type: string diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/README.md b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/README.md index da35d6b891239..74b9c6a034821 100644 --- a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/README.md +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/README.md @@ -2,16 +2,26 @@ This directory contains [OpenAPI specifications](https://swagger.io/specification/) for the [APM app API](https://www.elastic.co/guide/en/kibana/current/apm-api.html) in Kibana. -Included: +# OpenAPI (Experimental) -* [Agent Configuration API](https://www.elastic.co/guide/en/kibana/current/agent-config-api.html) -* [Annotation API](https://www.elastic.co/guide/en/kibana/current/apm-annotation-api.html) +The current self-contained spec file is available as `bundled.json` or `bundled.yaml` and can be used for online tools like those found at . +This spec is experimental and may be incomplete or change later. -Not included: +A guide about the openApi specification can be found at [https://swagger.io/docs/specification/about/](https://swagger.io/docs/specification/about/). -* [APM agent Key API](https://www.elastic.co/guide/en/kibana/current/agent-key-api.html) -* [RUM source map API](https://www.elastic.co/guide/en/kibana/current/rum-sourcemap-api.html) +## The `openapi` folder -The specifications for the included APIs are in the apm.yaml file in this directory. +* `entrypoint.yaml` is the overview file which pulls together all the paths and components. +* [Paths](paths/README.md): Defines each endpoint. A path can have one operation per http method. +* [Components](components/README.md): Defines reusable components. -These specifications are manually written. The missing ones will be included in the future. +## Tools + +Generate the `bundled` files by running the following commands: + +```bash +npx @redocly/cli bundle entrypoint.yaml --output bundled.yaml --ext yaml +npx @redocly/cli bundle entrypoint.yaml --output bundled.json --ext json +``` + +Then join these files with the rest of the Kibana APIs per `oas_docs/README.md` diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/bundled.json b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/bundled.json new file mode 100644 index 0000000000000..9fdcc3cdb6294 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/bundled.json @@ -0,0 +1,1827 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "APM UI", + "version": "1.0.0" + }, + "tags": [ + { + "name": "APM agent keys", + "description": "Configure APM agent keys to authorize requests from APM agents to the APM Server.\n" + }, + { + "name": "APM agent configuration", + "description": "Adjust APM agent configuration without need to redeploy your application.\n" + }, + { + "name": "APM sourcemaps", + "description": "Configure APM source maps." + }, + { + "name": "APM annotations", + "description": "Annotate visualizations in the APM app with significant events. Annotations enable you to easily see how events are impacting the performance of your applications.\n" + }, + { + "name": "APM server schema", + "description": "Create APM fleet server schema." + } + ], + "paths": { + "/api/apm/agent_keys": { + "post": { + "summary": "Create an APM agent key", + "description": "Create a new agent key for APM.", + "operationId": "createAgentKey", + "tags": [ + "APM agent keys" + ], + "parameters": [ + { + "$ref": "#/components/parameters/elastic_api_version" + }, + { + "$ref": "#/components/parameters/kbn_xsrf" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/agent_keys_object" + } + } + } + }, + "responses": { + "200": { + "description": "Agent key created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/agent_keys_response" + } + } + } + }, + "400": { + "description": "Bad Request response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/400_response" + } + } + } + }, + "401": { + "description": "Unauthorized response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/401_response" + } + } + } + }, + "403": { + "description": "Forbidden response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/403_response" + } + } + } + }, + "500": { + "description": "Internal Server Error response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/500_response" + } + } + } + } + } + } + }, + "/api/apm/services/{serviceName}/annotation/search": { + "get": { + "summary": "Search for annotations", + "description": "Search for annotations related to a specific service.", + "operationId": "getAnnotation", + "tags": [ + "APM annotations" + ], + "parameters": [ + { + "$ref": "#/components/parameters/elastic_api_version" + }, + { + "name": "serviceName", + "in": "path", + "required": true, + "description": "The name of the service", + "schema": { + "type": "string" + } + }, + { + "name": "environment", + "in": "query", + "required": false, + "description": "The environment to filter annotations by", + "schema": { + "type": "string" + } + }, + { + "name": "start", + "in": "query", + "required": false, + "description": "The start date for the search", + "schema": { + "type": "string" + } + }, + { + "name": "end", + "in": "query", + "required": false, + "description": "The end date for the search", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/annotation_search_response" + } + } + } + }, + "400": { + "description": "Bad Request response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/400_response" + } + } + } + }, + "401": { + "description": "Unauthorized response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/401_response" + } + } + } + }, + "500": { + "description": "Internal Server Error response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/500_response" + } + } + } + } + } + } + }, + "/api/apm/services/{serviceName}/annotation": { + "post": { + "summary": "Create a service annotation", + "description": "Create a new annotation for a specific service.", + "operationId": "createAnnotation", + "tags": [ + "APM annotations" + ], + "parameters": [ + { + "$ref": "#/components/parameters/elastic_api_version" + }, + { + "$ref": "#/components/parameters/kbn_xsrf" + }, + { + "name": "serviceName", + "in": "path", + "required": true, + "description": "The name of the service", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/create_annotation_object" + } + } + } + }, + "responses": { + "200": { + "description": "Annotation created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/create_annotation_response" + } + } + } + }, + "400": { + "description": "Bad Request response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/400_response" + } + } + } + }, + "401": { + "description": "Unauthorized response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/401_response" + } + } + } + }, + "403": { + "description": "Forbidden response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/403_response" + } + } + } + }, + "404": { + "description": "Not found response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/404_response" + } + } + } + } + } + } + }, + "/api/apm/settings/agent-configuration": { + "get": { + "summary": "Get a list of agent configurations", + "operationId": "getAgentConfigurations", + "tags": [ + "APM agent configuration" + ], + "parameters": [ + { + "$ref": "#/components/parameters/elastic_api_version" + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/agent_configurations_response" + } + } + } + }, + "400": { + "description": "Bad Request response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/400_response" + } + } + } + }, + "401": { + "description": "Unauthorized response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/401_response" + } + } + } + }, + "404": { + "description": "Not found response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/404_response" + } + } + } + } + } + }, + "delete": { + "summary": "Delete agent configuration", + "operationId": "deleteAgentConfiguration", + "tags": [ + "APM agent configuration" + ], + "parameters": [ + { + "$ref": "#/components/parameters/elastic_api_version" + }, + { + "$ref": "#/components/parameters/kbn_xsrf" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/service_object" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/delete_agent_configurations_response" + } + } + } + }, + "400": { + "description": "Bad Request response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/400_response" + } + } + } + }, + "401": { + "description": "Unauthorized response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/401_response" + } + } + } + }, + "403": { + "description": "Forbidden response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/403_response" + } + } + } + }, + "404": { + "description": "Not found response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/404_response" + } + } + } + } + } + }, + "put": { + "summary": "Create or update agent configuration", + "operationId": "createUpdateAgentConfiguration", + "tags": [ + "APM agent configuration" + ], + "parameters": [ + { + "$ref": "#/components/parameters/elastic_api_version" + }, + { + "$ref": "#/components/parameters/kbn_xsrf" + }, + { + "name": "overwrite", + "in": "query", + "description": "If the config exists ?overwrite=true is required", + "schema": { + "type": "boolean" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/agent_configuration_intake_object" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Bad Request response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/400_response" + } + } + } + }, + "401": { + "description": "Unauthorized response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/401_response" + } + } + } + }, + "403": { + "description": "Forbidden response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/403_response" + } + } + } + }, + "404": { + "description": "Not found response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/404_response" + } + } + } + } + } + } + }, + "/api/apm/settings/agent-configuration/view": { + "get": { + "summary": "Get single agent configuration", + "operationId": "getSingleAgentConfiguration", + "tags": [ + "APM agent configuration" + ], + "parameters": [ + { + "$ref": "#/components/parameters/elastic_api_version" + }, + { + "name": "name", + "in": "query", + "description": "Service name", + "schema": { + "type": "string" + }, + "example": "node" + }, + { + "name": "environment", + "in": "query", + "description": "Service environment", + "schema": { + "type": "string" + }, + "example": "prod" + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/single_agent_configuration_response" + } + } + } + }, + "400": { + "description": "Bad Request response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/400_response" + } + } + } + }, + "401": { + "description": "Unauthorized response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/401_response" + } + } + } + }, + "404": { + "description": "Not found response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/404_response" + } + } + } + } + } + } + }, + "/api/apm/settings/agent-configuration/search": { + "post": { + "summary": "Lookup single agent configuration", + "description": "This endpoint allows to search for single agent configuration and update 'applied_by_agent' field.\n", + "operationId": "searchSingleConfiguration", + "tags": [ + "APM agent configuration" + ], + "parameters": [ + { + "$ref": "#/components/parameters/elastic_api_version" + }, + { + "$ref": "#/components/parameters/kbn_xsrf" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/search_agent_configuration_object" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/search_agent_configuration_response" + } + } + } + }, + "400": { + "description": "Bad Request response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/400_response" + } + } + } + }, + "401": { + "description": "Unauthorized response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/401_response" + } + } + } + }, + "404": { + "description": "Not found response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/404_response" + } + } + } + } + } + } + }, + "/api/apm/settings/agent-configuration/environments": { + "get": { + "summary": "Get environments for service", + "operationId": "getEnvironmentsForService", + "tags": [ + "APM agent configuration" + ], + "parameters": [ + { + "$ref": "#/components/parameters/elastic_api_version" + }, + { + "name": "serviceName", + "in": "query", + "description": "The name of the service", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/service_environments_response" + } + } + } + }, + "400": { + "description": "Bad Request response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/400_response" + } + } + } + }, + "401": { + "description": "Unauthorized response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/401_response" + } + } + } + }, + "404": { + "description": "Not found response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/404_response" + } + } + } + } + } + } + }, + "/api/apm/settings/agent-configuration/agent_name": { + "get": { + "summary": "Get agent name for service", + "description": "Retrieve `agentName` for a service.", + "operationId": "getAgentNameForService", + "tags": [ + "APM agent configuration" + ], + "parameters": [ + { + "$ref": "#/components/parameters/elastic_api_version" + }, + { + "name": "serviceName", + "in": "query", + "description": "The name of the service", + "required": true, + "schema": { + "type": "string" + }, + "example": "node" + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/service_agent_name_response" + } + } + } + }, + "400": { + "description": "Bad Request response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/400_response" + } + } + } + }, + "401": { + "description": "Unauthorized response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/401_response" + } + } + } + }, + "404": { + "description": "Not found response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/404_response" + } + } + } + } + } + } + }, + "/api/apm/sourcemaps": { + "get": { + "summary": "Get source maps", + "description": "Returns an array of Fleet artifacts, including source map uploads.", + "operationId": "getSourceMaps", + "tags": [ + "APM sourcemaps" + ], + "parameters": [ + { + "$ref": "#/components/parameters/elastic_api_version" + }, + { + "name": "page", + "in": "query", + "description": "Page number", + "schema": { + "type": "number" + } + }, + { + "name": "perPage", + "in": "query", + "description": "Number of records per page", + "schema": { + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/source_maps_response" + } + } + } + }, + "400": { + "description": "Bad Request response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/400_response" + } + } + } + }, + "401": { + "description": "Unauthorized response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/401_response" + } + } + } + }, + "500": { + "description": "Internal Server Error response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/500_response" + } + } + } + }, + "501": { + "description": "Not Implemented response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/501_response" + } + } + } + } + } + }, + "post": { + "summary": "Upload source map", + "description": "Upload a source map for a specific service and version.", + "operationId": "uploadSourceMap", + "tags": [ + "APM sourcemaps" + ], + "parameters": [ + { + "$ref": "#/components/parameters/elastic_api_version" + }, + { + "$ref": "#/components/parameters/kbn_xsrf" + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/upload_source_map_object" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/upload_source_maps_response" + } + } + } + }, + "400": { + "description": "Bad Request response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/400_response" + } + } + } + }, + "401": { + "description": "Unauthorized response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/401_response" + } + } + } + }, + "403": { + "description": "Forbidden response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/403_response" + } + } + } + }, + "500": { + "description": "Internal Server Error response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/500_response" + } + } + } + }, + "501": { + "description": "Not Implemented response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/501_response" + } + } + } + } + } + } + }, + "/api/apm/sourcemaps/{id}": { + "delete": { + "summary": "Delete source map", + "description": "Delete a previously uploaded source map.", + "operationId": "deleteSourceMap", + "tags": [ + "APM sourcemaps" + ], + "parameters": [ + { + "$ref": "#/components/parameters/elastic_api_version" + }, + { + "$ref": "#/components/parameters/kbn_xsrf" + }, + { + "name": "id", + "in": "path", + "description": "Source map identifier", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Bad Request response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/400_response" + } + } + } + }, + "401": { + "description": "Unauthorized response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/401_response" + } + } + } + }, + "403": { + "description": "Forbidden response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/403_response" + } + } + } + }, + "500": { + "description": "Internal Server Error response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/500_response" + } + } + } + }, + "501": { + "description": "Not Implemented response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/501_response" + } + } + } + } + } + } + }, + "/api/apm/fleet/apm_server_schema": { + "post": { + "summary": "Save APM server schema", + "operationId": "saveApmServerSchema", + "tags": [ + "APM server schema" + ], + "parameters": [ + { + "$ref": "#/components/parameters/elastic_api_version" + }, + { + "$ref": "#/components/parameters/kbn_xsrf" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "schema": { + "type": "object", + "description": "Schema object", + "additionalProperties": true, + "example": { + "foo": "bar" + } + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Bad Request response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/400_response" + } + } + } + }, + "401": { + "description": "Unauthorized response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/401_response" + } + } + } + }, + "403": { + "description": "Forbidden response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/403_response" + } + } + } + }, + "404": { + "description": "Not found response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/404_response" + } + } + } + } + } + } + } + }, + "components": { + "parameters": { + "elastic_api_version": { + "description": "The version of the API to use", + "in": "header", + "name": "elastic-api-version", + "required": true, + "schema": { + "default": "2023-10-31", + "enum": [ + "2023-10-31" + ], + "type": "string" + } + }, + "kbn_xsrf": { + "description": "A required header to protect against CSRF attacks", + "in": "header", + "name": "kbn-xsrf", + "required": true, + "schema": { + "example": "true", + "type": "string" + } + } + }, + "schemas": { + "agent_keys_object": { + "type": "object", + "required": [ + "name", + "privileges" + ], + "properties": { + "name": { + "type": "string", + "description": "Agent name" + }, + "privileges": { + "type": "array", + "description": "Privileges configuration", + "items": { + "type": "string", + "enum": [ + "event:write", + "config_agent:read" + ] + } + } + } + }, + "agent_keys_response": { + "type": "object", + "properties": { + "agentKey": { + "type": "object", + "description": "Agent key", + "required": [ + "id", + "name", + "api_key", + "encoded" + ], + "properties": { + "expiration": { + "type": "integer", + "format": "int64" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "api_key": { + "type": "string" + }, + "encoded": { + "type": "string" + } + } + } + } + }, + "400_response": { + "type": "object", + "properties": { + "statusCode": { + "type": "number", + "example": 400, + "description": "Error status code" + }, + "error": { + "type": "string", + "example": "Not Found", + "description": "Error type" + }, + "message": { + "type": "string", + "example": "Not Found", + "description": "Error message" + } + } + }, + "401_response": { + "type": "object", + "properties": { + "statusCode": { + "type": "number", + "example": 401, + "description": "Error status code" + }, + "error": { + "type": "string", + "example": "Unauthorized", + "description": "Error type" + }, + "message": { + "type": "string", + "description": "Error message" + } + } + }, + "403_response": { + "type": "object", + "properties": { + "statusCode": { + "type": "number", + "example": 403, + "description": "Error status code" + }, + "error": { + "type": "string", + "example": "Forbidden", + "description": "Error type" + }, + "message": { + "type": "string", + "description": "Error message" + } + } + }, + "500_response": { + "type": "object", + "properties": { + "statusCode": { + "type": "number", + "example": 500, + "description": "Error status code" + }, + "error": { + "type": "string", + "example": "Internal Server Error", + "description": "Error type" + }, + "message": { + "type": "string", + "description": "Error message" + } + } + }, + "annotation_search_response": { + "type": "object", + "properties": { + "annotations": { + "type": "array", + "description": "Annotations", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "version" + ] + }, + "id": { + "type": "string" + }, + "@timestamp": { + "type": "number" + }, + "text": { + "type": "string" + } + } + } + } + } + }, + "create_annotation_object": { + "type": "object", + "required": [ + "@timestamp", + "service" + ], + "properties": { + "@timestamp": { + "type": "string", + "description": "Timestamp" + }, + "service": { + "type": "object", + "description": "Service", + "required": [ + "version" + ], + "properties": { + "version": { + "type": "string" + }, + "environment": { + "type": "string" + } + } + }, + "message": { + "type": "string", + "description": "Message" + }, + "tags": { + "type": "array", + "description": "Tags", + "items": { + "type": "string" + } + } + } + }, + "create_annotation_response": { + "type": "object", + "properties": { + "_id": { + "type": "string", + "description": "Identifier" + }, + "_index": { + "type": "string", + "description": "Index" + }, + "_source": { + "type": "object", + "description": "Response", + "properties": { + "annotation": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "message": { + "type": "string" + }, + "service": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "event": { + "type": "object", + "properties": { + "created": { + "type": "string" + } + } + }, + "@timestamp": { + "type": "string" + } + } + } + } + }, + "404_response": { + "type": "object", + "properties": { + "statusCode": { + "type": "number", + "example": 404, + "description": "Error status code" + }, + "error": { + "type": "string", + "example": "Not Found", + "description": "Error type" + }, + "message": { + "type": "string", + "example": "Not Found", + "description": "Error message" + } + } + }, + "service_object": { + "type": "object", + "description": "Service", + "properties": { + "name": { + "type": "string", + "example": "node", + "description": "Name" + }, + "environment": { + "type": "string", + "example": "prod", + "description": "Environment" + } + } + }, + "settings_object": { + "type": "object", + "description": "Agent configuration settings", + "additionalProperties": { + "type": "string" + } + }, + "agent_configuration_object": { + "type": "object", + "required": [ + "service", + "settings", + "@timestamp", + "etag" + ], + "description": "Agent configuration", + "properties": { + "agent_name": { + "type": "string", + "description": "Agent name" + }, + "service": { + "$ref": "#/components/schemas/service_object" + }, + "settings": { + "$ref": "#/components/schemas/settings_object" + }, + "@timestamp": { + "type": "number", + "example": 1730194190636, + "description": "Timestamp" + }, + "applied_by_agent": { + "type": "boolean", + "example": true, + "description": "Applied by agent" + }, + "etag": { + "type": "string", + "example": "0bc3b5ebf18fba8163fe4c96f491e3767a358f85", + "description": "Etag" + } + } + }, + "agent_configurations_response": { + "type": "object", + "properties": { + "configurations": { + "type": "array", + "description": "Agent configuration", + "items": { + "$ref": "#/components/schemas/agent_configuration_object" + } + } + } + }, + "agent_configuration_intake_object": { + "type": "object", + "required": [ + "service", + "settings" + ], + "properties": { + "agent_name": { + "type": "string", + "description": "Agent name" + }, + "service": { + "$ref": "#/components/schemas/service_object" + }, + "settings": { + "$ref": "#/components/schemas/settings_object" + } + } + }, + "delete_agent_configurations_response": { + "type": "object", + "properties": { + "result": { + "type": "string", + "description": "Result" + } + } + }, + "single_agent_configuration_response": { + "allOf": [ + { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + }, + { + "$ref": "#/components/schemas/agent_configuration_object" + } + ] + }, + "search_agent_configuration_object": { + "type": "object", + "required": [ + "service" + ], + "properties": { + "service": { + "$ref": "#/components/schemas/service_object" + }, + "etag": { + "type": "string", + "description": "If etags match then `applied_by_agent` field will be set to `true`", + "example": "0bc3b5ebf18fba8163fe4c96f491e3767a358f85" + }, + "mark_as_applied_by_agent": { + "type": "boolean", + "description": "`markAsAppliedByAgent=true` means \"force setting it to true regardless of etag\".\nThis is needed for Jaeger agent that doesn't have etags\n" + } + } + }, + "search_agent_configuration_response": { + "type": "object", + "properties": { + "_index": { + "type": "string", + "description": "Index" + }, + "_id": { + "type": "string", + "description": "Identifier" + }, + "_score": { + "type": "number", + "description": "Score" + }, + "_source": { + "$ref": "#/components/schemas/agent_configuration_object" + } + } + }, + "service_environment_object": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "ALL_OPTION_VALUE", + "description": "Service environment name" + }, + "alreadyConfigured": { + "type": "boolean", + "description": "Already configured" + } + } + }, + "service_environments_response": { + "type": "object", + "properties": { + "environments": { + "type": "array", + "description": "Service environment list", + "items": { + "$ref": "#/components/schemas/service_environment_object" + } + } + } + }, + "service_agent_name_response": { + "type": "object", + "properties": { + "agentName": { + "type": "string", + "description": "Agent name", + "example": "nodejs" + } + } + }, + "base_source_map_object": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Type" + }, + "identifier": { + "type": "string", + "description": "Identifier" + }, + "relative_url": { + "type": "string", + "description": "Relative URL" + }, + "created": { + "type": "string", + "description": "Created date" + }, + "id": { + "type": "string", + "description": "Identifier" + }, + "compressionAlgorithm": { + "type": "string", + "description": "Compression Algorithm" + }, + "decodedSha256": { + "type": "string", + "description": "Decoded SHA-256" + }, + "decodedSize": { + "type": "number", + "description": "Decoded size" + }, + "encodedSha256": { + "type": "string", + "description": "Encoded SHA-256" + }, + "encodedSize": { + "type": "number", + "description": "Encoded size" + }, + "encryptionAlgorithm": { + "type": "string", + "description": "Encryption Algorithm" + }, + "packageName": { + "type": "string", + "description": "Package name" + } + } + }, + "source_maps_response": { + "type": "object", + "properties": { + "artifacts": { + "type": "array", + "description": "Artifacts", + "items": { + "allOf": [ + { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "serviceName": { + "type": "string" + }, + "serviceVersion": { + "type": "string" + }, + "bundleFilepath": { + "type": "string" + }, + "sourceMap": { + "type": "object", + "properties": { + "version": { + "type": "number" + }, + "file": { + "type": "string" + }, + "sources": { + "type": "array", + "items": { + "type": "string" + } + }, + "sourcesContent": { + "type": "array", + "items": { + "type": "string" + } + }, + "mappings": { + "type": "string" + }, + "sourceRoot": { + "type": "string" + } + } + } + } + } + } + }, + { + "$ref": "#/components/schemas/base_source_map_object" + } + ] + } + } + } + }, + "501_response": { + "type": "object", + "properties": { + "statusCode": { + "type": "number", + "example": 501, + "description": "Error status code" + }, + "error": { + "type": "string", + "example": "Not Implemented", + "description": "Error type" + }, + "message": { + "type": "string", + "example": "Not Implemented", + "description": "Error message" + } + } + }, + "upload_source_map_object": { + "type": "object", + "required": [ + "service_name", + "service_version", + "bundle_filepath", + "sourcemap" + ], + "properties": { + "service_name": { + "type": "string", + "description": "The name of the service that the service map should apply to." + }, + "service_version": { + "type": "string", + "description": "The version of the service that the service map should apply to." + }, + "bundle_filepath": { + "type": "string", + "description": "The absolute path of the final bundle as used in the web application." + }, + "sourcemap": { + "type": "string", + "format": "binary", + "description": "The source map. String or file upload. It must follow the\n[source map revision 3 proposal](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k).\n" + } + } + }, + "upload_source_maps_response": { + "allOf": [ + { + "type": "object", + "properties": { + "body": { + "type": "string" + } + } + }, + { + "$ref": "#/components/schemas/base_source_map_object" + } + ] + } + } + } +} \ No newline at end of file diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/bundled.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/bundled.yaml new file mode 100644 index 0000000000000..caa71f6645e77 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/bundled.yaml @@ -0,0 +1,1162 @@ +openapi: 3.0.2 +info: + title: APM UI + version: 1.0.0 +tags: + - name: APM agent keys + description: | + Configure APM agent keys to authorize requests from APM agents to the APM Server. + - name: APM agent configuration + description: | + Adjust APM agent configuration without need to redeploy your application. + - name: APM sourcemaps + description: Configure APM source maps. + - name: APM annotations + description: | + Annotate visualizations in the APM app with significant events. Annotations enable you to easily see how events are impacting the performance of your applications. + - name: APM server schema + description: Create APM fleet server schema. +paths: + /api/apm/agent_keys: + post: + summary: Create an APM agent key + description: Create a new agent key for APM. + operationId: createAgentKey + tags: + - APM agent keys + parameters: + - $ref: '#/components/parameters/elastic_api_version' + - $ref: '#/components/parameters/kbn_xsrf' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/agent_keys_object' + responses: + '200': + description: Agent key created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/agent_keys_response' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '#/components/schemas/400_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/401_response' + '403': + description: Forbidden response + content: + application/json: + schema: + $ref: '#/components/schemas/403_response' + '500': + description: Internal Server Error response + content: + application/json: + schema: + $ref: '#/components/schemas/500_response' + /api/apm/services/{serviceName}/annotation/search: + get: + summary: Search for annotations + description: Search for annotations related to a specific service. + operationId: getAnnotation + tags: + - APM annotations + parameters: + - $ref: '#/components/parameters/elastic_api_version' + - name: serviceName + in: path + required: true + description: The name of the service + schema: + type: string + - name: environment + in: query + required: false + description: The environment to filter annotations by + schema: + type: string + - name: start + in: query + required: false + description: The start date for the search + schema: + type: string + - name: end + in: query + required: false + description: The end date for the search + schema: + type: string + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/annotation_search_response' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '#/components/schemas/400_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/401_response' + '500': + description: Internal Server Error response + content: + application/json: + schema: + $ref: '#/components/schemas/500_response' + /api/apm/services/{serviceName}/annotation: + post: + summary: Create a service annotation + description: Create a new annotation for a specific service. + operationId: createAnnotation + tags: + - APM annotations + parameters: + - $ref: '#/components/parameters/elastic_api_version' + - $ref: '#/components/parameters/kbn_xsrf' + - name: serviceName + in: path + required: true + description: The name of the service + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/create_annotation_object' + responses: + '200': + description: Annotation created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/create_annotation_response' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '#/components/schemas/400_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/401_response' + '403': + description: Forbidden response + content: + application/json: + schema: + $ref: '#/components/schemas/403_response' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '#/components/schemas/404_response' + /api/apm/settings/agent-configuration: + get: + summary: Get a list of agent configurations + operationId: getAgentConfigurations + tags: + - APM agent configuration + parameters: + - $ref: '#/components/parameters/elastic_api_version' + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/agent_configurations_response' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '#/components/schemas/400_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/401_response' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '#/components/schemas/404_response' + delete: + summary: Delete agent configuration + operationId: deleteAgentConfiguration + tags: + - APM agent configuration + parameters: + - $ref: '#/components/parameters/elastic_api_version' + - $ref: '#/components/parameters/kbn_xsrf' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/service_object' + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/delete_agent_configurations_response' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '#/components/schemas/400_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/401_response' + '403': + description: Forbidden response + content: + application/json: + schema: + $ref: '#/components/schemas/403_response' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '#/components/schemas/404_response' + put: + summary: Create or update agent configuration + operationId: createUpdateAgentConfiguration + tags: + - APM agent configuration + parameters: + - $ref: '#/components/parameters/elastic_api_version' + - $ref: '#/components/parameters/kbn_xsrf' + - name: overwrite + in: query + description: If the config exists ?overwrite=true is required + schema: + type: boolean + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/agent_configuration_intake_object' + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + additionalProperties: false + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '#/components/schemas/400_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/401_response' + '403': + description: Forbidden response + content: + application/json: + schema: + $ref: '#/components/schemas/403_response' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '#/components/schemas/404_response' + /api/apm/settings/agent-configuration/view: + get: + summary: Get single agent configuration + operationId: getSingleAgentConfiguration + tags: + - APM agent configuration + parameters: + - $ref: '#/components/parameters/elastic_api_version' + - name: name + in: query + description: Service name + schema: + type: string + example: node + - name: environment + in: query + description: Service environment + schema: + type: string + example: prod + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/single_agent_configuration_response' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '#/components/schemas/400_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/401_response' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '#/components/schemas/404_response' + /api/apm/settings/agent-configuration/search: + post: + summary: Lookup single agent configuration + description: | + This endpoint allows to search for single agent configuration and update 'applied_by_agent' field. + operationId: searchSingleConfiguration + tags: + - APM agent configuration + parameters: + - $ref: '#/components/parameters/elastic_api_version' + - $ref: '#/components/parameters/kbn_xsrf' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/search_agent_configuration_object' + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/search_agent_configuration_response' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '#/components/schemas/400_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/401_response' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '#/components/schemas/404_response' + /api/apm/settings/agent-configuration/environments: + get: + summary: Get environments for service + operationId: getEnvironmentsForService + tags: + - APM agent configuration + parameters: + - $ref: '#/components/parameters/elastic_api_version' + - name: serviceName + in: query + description: The name of the service + schema: + type: string + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/service_environments_response' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '#/components/schemas/400_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/401_response' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '#/components/schemas/404_response' + /api/apm/settings/agent-configuration/agent_name: + get: + summary: Get agent name for service + description: Retrieve `agentName` for a service. + operationId: getAgentNameForService + tags: + - APM agent configuration + parameters: + - $ref: '#/components/parameters/elastic_api_version' + - name: serviceName + in: query + description: The name of the service + required: true + schema: + type: string + example: node + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/service_agent_name_response' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '#/components/schemas/400_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/401_response' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '#/components/schemas/404_response' + /api/apm/sourcemaps: + get: + summary: Get source maps + description: Returns an array of Fleet artifacts, including source map uploads. + operationId: getSourceMaps + tags: + - APM sourcemaps + parameters: + - $ref: '#/components/parameters/elastic_api_version' + - name: page + in: query + description: Page number + schema: + type: number + - name: perPage + in: query + description: Number of records per page + schema: + type: number + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/source_maps_response' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '#/components/schemas/400_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/401_response' + '500': + description: Internal Server Error response + content: + application/json: + schema: + $ref: '#/components/schemas/500_response' + '501': + description: Not Implemented response + content: + application/json: + schema: + $ref: '#/components/schemas/501_response' + post: + summary: Upload source map + description: Upload a source map for a specific service and version. + operationId: uploadSourceMap + tags: + - APM sourcemaps + parameters: + - $ref: '#/components/parameters/elastic_api_version' + - $ref: '#/components/parameters/kbn_xsrf' + requestBody: + required: true + content: + multipart/form-data: + schema: + $ref: '#/components/schemas/upload_source_map_object' + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/upload_source_maps_response' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '#/components/schemas/400_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/401_response' + '403': + description: Forbidden response + content: + application/json: + schema: + $ref: '#/components/schemas/403_response' + '500': + description: Internal Server Error response + content: + application/json: + schema: + $ref: '#/components/schemas/500_response' + '501': + description: Not Implemented response + content: + application/json: + schema: + $ref: '#/components/schemas/501_response' + /api/apm/sourcemaps/{id}: + delete: + summary: Delete source map + description: Delete a previously uploaded source map. + operationId: deleteSourceMap + tags: + - APM sourcemaps + parameters: + - $ref: '#/components/parameters/elastic_api_version' + - $ref: '#/components/parameters/kbn_xsrf' + - name: id + in: path + description: Source map identifier + required: true + schema: + type: string + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + additionalProperties: false + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '#/components/schemas/400_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/401_response' + '403': + description: Forbidden response + content: + application/json: + schema: + $ref: '#/components/schemas/403_response' + '500': + description: Internal Server Error response + content: + application/json: + schema: + $ref: '#/components/schemas/500_response' + '501': + description: Not Implemented response + content: + application/json: + schema: + $ref: '#/components/schemas/501_response' + /api/apm/fleet/apm_server_schema: + post: + summary: Save APM server schema + operationId: saveApmServerSchema + tags: + - APM server schema + parameters: + - $ref: '#/components/parameters/elastic_api_version' + - $ref: '#/components/parameters/kbn_xsrf' + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + schema: + type: object + description: Schema object + additionalProperties: true + example: + foo: bar + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + additionalProperties: false + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '#/components/schemas/400_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/401_response' + '403': + description: Forbidden response + content: + application/json: + schema: + $ref: '#/components/schemas/403_response' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '#/components/schemas/404_response' +components: + parameters: + elastic_api_version: + description: The version of the API to use + in: header + name: elastic-api-version + required: true + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + kbn_xsrf: + description: A required header to protect against CSRF attacks + in: header + name: kbn-xsrf + required: true + schema: + example: 'true' + type: string + schemas: + agent_keys_object: + type: object + required: + - name + - privileges + properties: + name: + type: string + description: Agent name + privileges: + type: array + description: Privileges configuration + items: + type: string + enum: + - event:write + - config_agent:read + agent_keys_response: + type: object + properties: + agentKey: + type: object + description: Agent key + required: + - id + - name + - api_key + - encoded + properties: + expiration: + type: integer + format: int64 + id: + type: string + name: + type: string + api_key: + type: string + encoded: + type: string + 400_response: + type: object + properties: + statusCode: + type: number + example: 400 + description: Error status code + error: + type: string + example: Not Found + description: Error type + message: + type: string + example: Not Found + description: Error message + 401_response: + type: object + properties: + statusCode: + type: number + example: 401 + description: Error status code + error: + type: string + example: Unauthorized + description: Error type + message: + type: string + description: Error message + 403_response: + type: object + properties: + statusCode: + type: number + example: 403 + description: Error status code + error: + type: string + example: Forbidden + description: Error type + message: + type: string + description: Error message + 500_response: + type: object + properties: + statusCode: + type: number + example: 500 + description: Error status code + error: + type: string + example: Internal Server Error + description: Error type + message: + type: string + description: Error message + annotation_search_response: + type: object + properties: + annotations: + type: array + description: Annotations + items: + type: object + properties: + type: + type: string + enum: + - version + id: + type: string + '@timestamp': + type: number + text: + type: string + create_annotation_object: + type: object + required: + - '@timestamp' + - service + properties: + '@timestamp': + type: string + description: Timestamp + service: + type: object + description: Service + required: + - version + properties: + version: + type: string + environment: + type: string + message: + type: string + description: Message + tags: + type: array + description: Tags + items: + type: string + create_annotation_response: + type: object + properties: + _id: + type: string + description: Identifier + _index: + type: string + description: Index + _source: + type: object + description: Response + properties: + annotation: + type: object + properties: + type: + type: string + title: + type: string + tags: + type: array + items: + type: string + message: + type: string + service: + type: object + properties: + name: + type: string + environment: + type: string + version: + type: string + event: + type: object + properties: + created: + type: string + '@timestamp': + type: string + 404_response: + type: object + properties: + statusCode: + type: number + example: 404 + description: Error status code + error: + type: string + example: Not Found + description: Error type + message: + type: string + example: Not Found + description: Error message + service_object: + type: object + description: Service + properties: + name: + type: string + example: node + description: Name + environment: + type: string + example: prod + description: Environment + settings_object: + type: object + description: Agent configuration settings + additionalProperties: + type: string + agent_configuration_object: + type: object + required: + - service + - settings + - '@timestamp' + - etag + description: Agent configuration + properties: + agent_name: + type: string + description: Agent name + service: + $ref: '#/components/schemas/service_object' + settings: + $ref: '#/components/schemas/settings_object' + '@timestamp': + type: number + example: 1730194190636 + description: Timestamp + applied_by_agent: + type: boolean + example: true + description: Applied by agent + etag: + type: string + example: 0bc3b5ebf18fba8163fe4c96f491e3767a358f85 + description: Etag + agent_configurations_response: + type: object + properties: + configurations: + type: array + description: Agent configuration + items: + $ref: '#/components/schemas/agent_configuration_object' + agent_configuration_intake_object: + type: object + required: + - service + - settings + properties: + agent_name: + type: string + description: Agent name + service: + $ref: '#/components/schemas/service_object' + settings: + $ref: '#/components/schemas/settings_object' + delete_agent_configurations_response: + type: object + properties: + result: + type: string + description: Result + single_agent_configuration_response: + allOf: + - type: object + required: + - id + properties: + id: + type: string + - $ref: '#/components/schemas/agent_configuration_object' + search_agent_configuration_object: + type: object + required: + - service + properties: + service: + $ref: '#/components/schemas/service_object' + etag: + type: string + description: If etags match then `applied_by_agent` field will be set to `true` + example: 0bc3b5ebf18fba8163fe4c96f491e3767a358f85 + mark_as_applied_by_agent: + type: boolean + description: | + `markAsAppliedByAgent=true` means "force setting it to true regardless of etag". + This is needed for Jaeger agent that doesn't have etags + search_agent_configuration_response: + type: object + properties: + _index: + type: string + description: Index + _id: + type: string + description: Identifier + _score: + type: number + description: Score + _source: + $ref: '#/components/schemas/agent_configuration_object' + service_environment_object: + type: object + properties: + name: + type: string + example: ALL_OPTION_VALUE + description: Service environment name + alreadyConfigured: + type: boolean + description: Already configured + service_environments_response: + type: object + properties: + environments: + type: array + description: Service environment list + items: + $ref: '#/components/schemas/service_environment_object' + service_agent_name_response: + type: object + properties: + agentName: + type: string + description: Agent name + example: nodejs + base_source_map_object: + type: object + properties: + type: + type: string + description: Type + identifier: + type: string + description: Identifier + relative_url: + type: string + description: Relative URL + created: + type: string + description: Created date + id: + type: string + description: Identifier + compressionAlgorithm: + type: string + description: Compression Algorithm + decodedSha256: + type: string + description: Decoded SHA-256 + decodedSize: + type: number + description: Decoded size + encodedSha256: + type: string + description: Encoded SHA-256 + encodedSize: + type: number + description: Encoded size + encryptionAlgorithm: + type: string + description: Encryption Algorithm + packageName: + type: string + description: Package name + source_maps_response: + type: object + properties: + artifacts: + type: array + description: Artifacts + items: + allOf: + - type: object + properties: + body: + type: object + properties: + serviceName: + type: string + serviceVersion: + type: string + bundleFilepath: + type: string + sourceMap: + type: object + properties: + version: + type: number + file: + type: string + sources: + type: array + items: + type: string + sourcesContent: + type: array + items: + type: string + mappings: + type: string + sourceRoot: + type: string + - $ref: '#/components/schemas/base_source_map_object' + 501_response: + type: object + properties: + statusCode: + type: number + example: 501 + description: Error status code + error: + type: string + example: Not Implemented + description: Error type + message: + type: string + example: Not Implemented + description: Error message + upload_source_map_object: + type: object + required: + - service_name + - service_version + - bundle_filepath + - sourcemap + properties: + service_name: + type: string + description: The name of the service that the service map should apply to. + service_version: + type: string + description: The version of the service that the service map should apply to. + bundle_filepath: + type: string + description: The absolute path of the final bundle as used in the web application. + sourcemap: + type: string + format: binary + description: | + The source map. String or file upload. It must follow the + [source map revision 3 proposal](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k). + upload_source_maps_response: + allOf: + - type: object + properties: + body: + type: string + - $ref: '#/components/schemas/base_source_map_object' diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/README.md b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/README.md new file mode 100644 index 0000000000000..6beadcd86e1e9 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/README.md @@ -0,0 +1,7 @@ +Reusable components +=========== + + - `examples` - reusable [Example objects](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.2.md#example-object) + - `headers` - reusable [Header objects](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#header-object) + - `parameters` - reusable [Parameter objects](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#parameter-object) + - `schemas` - reusable [Schema objects](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.2.md#schema-object) diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/headers/elastic_api_version.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/headers/elastic_api_version.yaml new file mode 100644 index 0000000000000..b11a093b7b581 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/headers/elastic_api_version.yaml @@ -0,0 +1,9 @@ +description: The version of the API to use +in: header +name: elastic-api-version +required: true +schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/headers/kbn_xsrf.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/headers/kbn_xsrf.yaml new file mode 100644 index 0000000000000..25fcd49c04e65 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/headers/kbn_xsrf.yaml @@ -0,0 +1,7 @@ +description: A required header to protect against CSRF attacks +in: header +name: kbn-xsrf +required: true +schema: + example: 'true' + type: string diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/400_response.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/400_response.yaml new file mode 100644 index 0000000000000..3f09203cd49d3 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/400_response.yaml @@ -0,0 +1,14 @@ +type: object +properties: + statusCode: + type: number + example: 400 + description: Error status code + error: + type: string + example: Not Found + description: Error type + message: + type: string + example: Not Found + description: Error message diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/401_response.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/401_response.yaml new file mode 100644 index 0000000000000..cf5afb3482e6c --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/401_response.yaml @@ -0,0 +1,13 @@ +type: object +properties: + statusCode: + type: number + example: 401 + description: Error status code + error: + type: string + example: Unauthorized + description: Error type + message: + type: string + description: Error message diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/403_response.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/403_response.yaml new file mode 100644 index 0000000000000..f04184dcb1bb2 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/403_response.yaml @@ -0,0 +1,13 @@ +type: object +properties: + statusCode: + type: number + example: 403 + description: Error status code + error: + type: string + example: Forbidden + description: Error type + message: + type: string + description: Error message diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/404_response.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/404_response.yaml new file mode 100644 index 0000000000000..9e539dcbda096 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/404_response.yaml @@ -0,0 +1,14 @@ +type: object +properties: + statusCode: + type: number + example: 404 + description: Error status code + error: + type: string + example: Not Found + description: Error type + message: + type: string + example: Not Found + description: Error message diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/500_response.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/500_response.yaml new file mode 100644 index 0000000000000..e952739138146 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/500_response.yaml @@ -0,0 +1,13 @@ +type: object +properties: + statusCode: + type: number + example: 500 + description: Error status code + error: + type: string + example: Internal Server Error + description: Error type + message: + type: string + description: Error message diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/501_response.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/501_response.yaml new file mode 100644 index 0000000000000..f5a1545f7183e --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/501_response.yaml @@ -0,0 +1,14 @@ +type: object +properties: + statusCode: + type: number + example: 501 + description: Error status code + error: + type: string + example: Not Implemented + description: Error type + message: + type: string + example: Not Implemented + description: Error message diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/agent_configuration_intake_object.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/agent_configuration_intake_object.yaml new file mode 100644 index 0000000000000..0be4a414cd35a --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/agent_configuration_intake_object.yaml @@ -0,0 +1,12 @@ +type: object +required: + - service + - settings +properties: + agent_name: + type: string + description: Agent name + service: + $ref: 'service_object.yaml' + settings: + $ref: 'settings_object.yaml' diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/agent_configuration_object.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/agent_configuration_object.yaml new file mode 100644 index 0000000000000..8dfa922c25874 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/agent_configuration_object.yaml @@ -0,0 +1,27 @@ +type: object +required: + - service + - settings + - '@timestamp' + - etag +description: Agent configuration +properties: + agent_name: + type: string + description: Agent name + service: + $ref: 'service_object.yaml' + settings: + $ref: 'settings_object.yaml' + '@timestamp': + type: number + example: 1730194190636 + description: Timestamp + applied_by_agent: + type: boolean + example: true + description: Applied by agent + etag: + type: string + example: 0bc3b5ebf18fba8163fe4c96f491e3767a358f85 + description: Etag diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/agent_configurations_response.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/agent_configurations_response.yaml new file mode 100644 index 0000000000000..a6bdb51466fb4 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/agent_configurations_response.yaml @@ -0,0 +1,7 @@ +type: object +properties: + configurations: + type: array + description: Agent configuration + items: + $ref: 'agent_configuration_object.yaml' diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/agent_keys_object.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/agent_keys_object.yaml new file mode 100644 index 0000000000000..c5e248471db90 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/agent_keys_object.yaml @@ -0,0 +1,16 @@ +type: object +required: + - name + - privileges +properties: + name: + type: string + description: Agent name + privileges: + type: array + description: Privileges configuration + items: + type: string + enum: + - event:write + - config_agent:read diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/agent_keys_response.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/agent_keys_response.yaml new file mode 100644 index 0000000000000..3507dae535faf --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/agent_keys_response.yaml @@ -0,0 +1,22 @@ +type: object +properties: + agentKey: + type: object + description: Agent key + required: + - id + - name + - api_key + - encoded + properties: + expiration: + type: integer + format: int64 + id: + type: string + name: + type: string + api_key: + type: string + encoded: + type: string diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/annotation_search_response.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/annotation_search_response.yaml new file mode 100644 index 0000000000000..7827f17ffb979 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/annotation_search_response.yaml @@ -0,0 +1,18 @@ +type: object +properties: + annotations: + type: array + description: Annotations + items: + type: object + properties: + type: + type: string + enum: + - version + id: + type: string + "@timestamp": + type: number + text: + type: string diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/base_source_map_object.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/base_source_map_object.yaml new file mode 100644 index 0000000000000..f642c933f2b71 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/base_source_map_object.yaml @@ -0,0 +1,38 @@ +type: object +properties: + type: + type: string + description: Type + identifier: + type: string + description: Identifier + relative_url: + type: string + description: Relative URL + created: + type: string + description: Created date + id: + type: string + description: Identifier + compressionAlgorithm: + type: string + description: Compression Algorithm + decodedSha256: + type: string + description: Decoded SHA-256 + decodedSize: + type: number + description: Decoded size + encodedSha256: + type: string + description: Encoded SHA-256 + encodedSize: + type: number + description: Encoded size + encryptionAlgorithm: + type: string + description: Encryption Algorithm + packageName: + type: string + description: Package name diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/create_annotation_object.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/create_annotation_object.yaml new file mode 100644 index 0000000000000..6a8f8ba9b96d0 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/create_annotation_object.yaml @@ -0,0 +1,26 @@ +type: object +required: + - '@timestamp' + - service +properties: + '@timestamp': + type: string + description: Timestamp + service: + type: object + description: Service + required: + - version + properties: + version: + type: string + environment: + type: string + message: + type: string + description: Message + tags: + type: array + description: Tags + items: + type: string diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/create_annotation_response.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/create_annotation_response.yaml new file mode 100644 index 0000000000000..4738406a1e765 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/create_annotation_response.yaml @@ -0,0 +1,41 @@ +type: object +properties: + _id: + type: string + description: Identifier + _index: + type: string + description: Index + _source: + type: object + description: Response + properties: + annotation: + type: object + properties: + type: + type: string + title: + type: string + tags: + type: array + items: + type: string + message: + type: string + service: + type: object + properties: + name: + type: string + environment: + type: string + version: + type: string + event: + type: object + properties: + created: + type: string + '@timestamp': + type: string diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/delete_agent_configurations_response.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/delete_agent_configurations_response.yaml new file mode 100644 index 0000000000000..3dc7c58998b1f --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/delete_agent_configurations_response.yaml @@ -0,0 +1,5 @@ +type: object +properties: + result: + type: string + description: Result diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/search_agent_configuration_object.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/search_agent_configuration_object.yaml new file mode 100644 index 0000000000000..abbbf91b77b89 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/search_agent_configuration_object.yaml @@ -0,0 +1,15 @@ +type: object +required: + - service +properties: + service: + $ref: 'service_object.yaml' + etag: + type: string + description: If etags match then `applied_by_agent` field will be set to `true` + example: 0bc3b5ebf18fba8163fe4c96f491e3767a358f85 + mark_as_applied_by_agent: + type: boolean + description: | + `markAsAppliedByAgent=true` means "force setting it to true regardless of etag". + This is needed for Jaeger agent that doesn't have etags diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/search_agent_configuration_response.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/search_agent_configuration_response.yaml new file mode 100644 index 0000000000000..d0a5ff1d91a78 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/search_agent_configuration_response.yaml @@ -0,0 +1,13 @@ +type: object +properties: + _index: + type: string + description: Index + _id: + type: string + description: Identifier + _score: + type: number + description: Score + _source: + $ref: 'agent_configuration_object.yaml' diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/service_agent_name_response.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/service_agent_name_response.yaml new file mode 100644 index 0000000000000..b67c34b65df8e --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/service_agent_name_response.yaml @@ -0,0 +1,6 @@ +type: object +properties: + agentName: + type: string + description: Agent name + example: nodejs diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/service_environment_object.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/service_environment_object.yaml new file mode 100644 index 0000000000000..0f7f11371e59c --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/service_environment_object.yaml @@ -0,0 +1,9 @@ +type: object +properties: + name: + type: string + example: ALL_OPTION_VALUE + description: Service environment name + alreadyConfigured: + type: boolean + description: Already configured diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/service_environments_response.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/service_environments_response.yaml new file mode 100644 index 0000000000000..1b8cdc1cab48e --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/service_environments_response.yaml @@ -0,0 +1,7 @@ +type: object +properties: + environments: + type: array + description: Service environment list + items: + $ref: 'service_environment_object.yaml' diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/service_object.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/service_object.yaml new file mode 100644 index 0000000000000..c73dd8bc0eb19 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/service_object.yaml @@ -0,0 +1,11 @@ +type: object +description: Service +properties: + name: + type: string + example: node + description: Name + environment: + type: string + example: prod + description: Environment diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/settings_object.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/settings_object.yaml new file mode 100644 index 0000000000000..cf09f1b6254bd --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/settings_object.yaml @@ -0,0 +1,4 @@ +type: object +description: Agent configuration settings +additionalProperties: + type: string diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/single_agent_configuration_response.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/single_agent_configuration_response.yaml new file mode 100644 index 0000000000000..4ef312304cc60 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/single_agent_configuration_response.yaml @@ -0,0 +1,8 @@ +allOf: + - type: object + required: + - id + properties: + id: + type: string + - $ref: 'agent_configuration_object.yaml' diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/source_maps_response.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/source_maps_response.yaml new file mode 100644 index 0000000000000..f5590110fecd2 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/source_maps_response.yaml @@ -0,0 +1,38 @@ +type: object +properties: + artifacts: + type: array + description: Artifacts + items: + allOf: + - type: object + properties: + body: + type: object + properties: + serviceName: + type: string + serviceVersion: + type: string + bundleFilepath: + type: string + sourceMap: + type: object + properties: + version: + type: number + file: + type: string + sources: + type: array + items: + type: string + sourcesContent: + type: array + items: + type: string + mappings: + type: string + sourceRoot: + type: string + - $ref: 'base_source_map_object.yaml' diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/upload_source_map_object.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/upload_source_map_object.yaml new file mode 100644 index 0000000000000..00483e3606df0 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/upload_source_map_object.yaml @@ -0,0 +1,22 @@ +type: object +required: + - service_name + - service_version + - bundle_filepath + - sourcemap +properties: + service_name: + type: string + description: The name of the service that the service map should apply to. + service_version: + type: string + description: The version of the service that the service map should apply to. + bundle_filepath: + type: string + description: The absolute path of the final bundle as used in the web application. + sourcemap: + type: string + format: binary + description: | + The source map. String or file upload. It must follow the + [source map revision 3 proposal](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k). diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/upload_source_maps_response.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/upload_source_maps_response.yaml new file mode 100644 index 0000000000000..6b677374de4ab --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/components/schemas/upload_source_maps_response.yaml @@ -0,0 +1,6 @@ +allOf: + - type: object + properties: + body: + type: string + - $ref: 'base_source_map_object.yaml' diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/entrypoint.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/entrypoint.yaml new file mode 100644 index 0000000000000..abb21fb980a9a --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/entrypoint.yaml @@ -0,0 +1,42 @@ +openapi: 3.0.2 +info: + title: APM UI + version: 1.0.0 +tags: + - name: APM agent keys + description: > + Configure APM agent keys to authorize requests from APM agents to the APM Server. + - name: APM agent configuration + description: > + Adjust APM agent configuration without need to redeploy your application. + - name: APM sourcemaps + description: Configure APM source maps. + - name: APM annotations + description: > + Annotate visualizations in the APM app with significant events. + Annotations enable you to easily see how events are impacting the performance of your applications. + - name: APM server schema + description: Create APM fleet server schema. +paths: + /api/apm/agent_keys: + $ref: 'paths/api@apm@agent_keys.yaml' + /api/apm/services/{serviceName}/annotation/search: + $ref: 'paths/api@apm@services@{service_name}@annotation@search.yaml' + /api/apm/services/{serviceName}/annotation: + $ref: 'paths/api@apm@services@{service_name}@annotation.yaml' + /api/apm/settings/agent-configuration: + $ref: 'paths/api@apm@settings@agent_configuration.yaml' + /api/apm/settings/agent-configuration/view: + $ref: 'paths/api@apm@settings@agent_configuration@view.yaml' + /api/apm/settings/agent-configuration/search: + $ref: 'paths/api@apm@settings@agent_configuration@search.yaml' + /api/apm/settings/agent-configuration/environments: + $ref: 'paths/api@apm@settings@agent_configuration@environments.yaml' + /api/apm/settings/agent-configuration/agent_name: + $ref: 'paths/api@apm@settings@agent_configuration@agent_name.yaml' + /api/apm/sourcemaps: + $ref: 'paths/api@apm@sourcemaps.yaml' + /api/apm/sourcemaps/{id}: + $ref: 'paths/api@apm@sourcemaps@{id}.yaml' + /api/apm/fleet/apm_server_schema: + $ref: 'paths/api@apm@fleet@apm_server_schema.yaml' diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/README.md b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/README.md new file mode 100644 index 0000000000000..b7818c8474fc8 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/README.md @@ -0,0 +1,10 @@ +Paths +===== + +Each path definition for which there is a specification exists within this folder. + +These files currently use the following conventions: + +* path separator token (e.g. `@`) is included in the file name +* path parameter (e.g. `{example}`) is included in the file name +* there is one file per path; each file can contain multiple operations diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@agent_keys.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@agent_keys.yaml new file mode 100644 index 0000000000000..46b1588517761 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@agent_keys.yaml @@ -0,0 +1,46 @@ +post: + summary: Create an APM agent key + description: Create a new agent key for APM. + operationId: createAgentKey + tags: + - APM agent keys + parameters: + - $ref: '../components/headers/elastic_api_version.yaml' + - $ref: '../components/headers/kbn_xsrf.yaml' + requestBody: + required: true + content: + application/json: + schema: + $ref: '../components/schemas/agent_keys_object.yaml' + responses: + "200": + description: Agent key created successfully + content: + application/json: + schema: + $ref: '../components/schemas/agent_keys_response.yaml' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '../components/schemas/400_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/401_response.yaml' + '403': + description: Forbidden response + content: + application/json: + schema: + $ref: '../components/schemas/403_response.yaml' + '500': + description: Internal Server Error response + content: + application/json: + schema: + $ref: '../components/schemas/500_response.yaml' diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@fleet@apm_server_schema.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@fleet@apm_server_schema.yaml new file mode 100644 index 0000000000000..2c4629b44a211 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@fleet@apm_server_schema.yaml @@ -0,0 +1,53 @@ +post: + summary: Save APM server schema + operationId: saveApmServerSchema + tags: + - APM server schema + parameters: + - $ref: '../components/headers/elastic_api_version.yaml' + - $ref: '../components/headers/kbn_xsrf.yaml' + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + schema: + type: object + description: Schema object + additionalProperties: true + example: + foo: "bar" + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + additionalProperties: false + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '../components/schemas/400_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/401_response.yaml' + '403': + description: Forbidden response + content: + application/json: + schema: + $ref: '../components/schemas/403_response.yaml' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '../components/schemas/404_response.yaml' diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@services@{service_name}@annotation.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@services@{service_name}@annotation.yaml new file mode 100644 index 0000000000000..3894bd6da2015 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@services@{service_name}@annotation.yaml @@ -0,0 +1,52 @@ +post: + summary: Create a service annotation + description: Create a new annotation for a specific service. + operationId: createAnnotation + tags: + - APM annotations + parameters: + - $ref: '../components/headers/elastic_api_version.yaml' + - $ref: '../components/headers/kbn_xsrf.yaml' + - name: serviceName + in: path + required: true + description: The name of the service + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '../components/schemas/create_annotation_object.yaml' + responses: + '200': + description: Annotation created successfully + content: + application/json: + schema: + $ref: '../components/schemas/create_annotation_response.yaml' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '../components/schemas/400_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/401_response.yaml' + '403': + description: Forbidden response + content: + application/json: + schema: + $ref: '../components/schemas/403_response.yaml' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '../components/schemas/404_response.yaml' diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@services@{service_name}@annotation@search.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@services@{service_name}@annotation@search.yaml new file mode 100644 index 0000000000000..0f1391be89806 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@services@{service_name}@annotation@search.yaml @@ -0,0 +1,57 @@ +get: + summary: Search for annotations + description: Search for annotations related to a specific service. + operationId: getAnnotation + tags: + - APM annotations + parameters: + - $ref: '../components/headers/elastic_api_version.yaml' + - name: serviceName + in: path + required: true + description: The name of the service + schema: + type: string + - name: environment + in: query + required: false + description: The environment to filter annotations by + schema: + type: string + - name: start + in: query + required: false + description: The start date for the search + schema: + type: string + - name: end + in: query + required: false + description: The end date for the search + schema: + type: string + responses: + "200": + description: Successful response + content: + application/json: + schema: + $ref: '../components/schemas/annotation_search_response.yaml' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '../components/schemas/400_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/401_response.yaml' + '500': + description: Internal Server Error response + content: + application/json: + schema: + $ref: '../components/schemas/500_response.yaml' diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@settings@agent_configuration.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@settings@agent_configuration.yaml new file mode 100644 index 0000000000000..f508e855a3883 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@settings@agent_configuration.yaml @@ -0,0 +1,128 @@ +get: + summary: Get a list of agent configurations + operationId: getAgentConfigurations + tags: + - APM agent configuration + parameters: + - $ref: '../components/headers/elastic_api_version.yaml' + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '../components/schemas/agent_configurations_response.yaml' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '../components/schemas/400_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/401_response.yaml' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '../components/schemas/404_response.yaml' +delete: + summary: Delete agent configuration + operationId: deleteAgentConfiguration + tags: + - APM agent configuration + parameters: + - $ref: '../components/headers/elastic_api_version.yaml' + - $ref: '../components/headers/kbn_xsrf.yaml' + requestBody: + required: true + content: + application/json: + schema: + $ref: '../components/schemas/service_object.yaml' + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '../components/schemas/delete_agent_configurations_response.yaml' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '../components/schemas/400_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/401_response.yaml' + '403': + description: Forbidden response + content: + application/json: + schema: + $ref: '../components/schemas/403_response.yaml' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '../components/schemas/404_response.yaml' +put: + summary: Create or update agent configuration + operationId: createUpdateAgentConfiguration + tags: + - APM agent configuration + parameters: + - $ref: '../components/headers/elastic_api_version.yaml' + - $ref: '../components/headers/kbn_xsrf.yaml' + - name: overwrite + in: query + description: If the config exists ?overwrite=true is required + schema: + type: boolean + requestBody: + required: true + content: + application/json: + schema: + $ref: '../components/schemas/agent_configuration_intake_object.yaml' + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + additionalProperties: false + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '../components/schemas/400_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/401_response.yaml' + '403': + description: Forbidden response + content: + application/json: + schema: + $ref: '../components/schemas/403_response.yaml' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '../components/schemas/404_response.yaml' diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@settings@agent_configuration@agent_name.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@settings@agent_configuration@agent_name.yaml new file mode 100644 index 0000000000000..4ad10fbb00833 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@settings@agent_configuration@agent_name.yaml @@ -0,0 +1,40 @@ +get: + summary: Get agent name for service + description: Retrieve `agentName` for a service. + operationId: getAgentNameForService + tags: + - APM agent configuration + parameters: + - $ref: '../components/headers/elastic_api_version.yaml' + - name: serviceName + in: query + description: The name of the service + required: true + schema: + type: string + example: node + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '../components/schemas/service_agent_name_response.yaml' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '../components/schemas/400_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/401_response.yaml' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '../components/schemas/404_response.yaml' diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@settings@agent_configuration@environments.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@settings@agent_configuration@environments.yaml new file mode 100644 index 0000000000000..c0791b92ac148 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@settings@agent_configuration@environments.yaml @@ -0,0 +1,37 @@ +get: + summary: Get environments for service + operationId: getEnvironmentsForService + tags: + - APM agent configuration + parameters: + - $ref: '../components/headers/elastic_api_version.yaml' + - name: serviceName + in: query + description: The name of the service + schema: + type: string + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '../components/schemas/service_environments_response.yaml' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '../components/schemas/400_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/401_response.yaml' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '../components/schemas/404_response.yaml' diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@settings@agent_configuration@search.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@settings@agent_configuration@search.yaml new file mode 100644 index 0000000000000..8ae4ce975fc08 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@settings@agent_configuration@search.yaml @@ -0,0 +1,41 @@ +post: + summary: Lookup single agent configuration + description: | + This endpoint allows to search for single agent configuration and update 'applied_by_agent' field. + operationId: searchSingleConfiguration + tags: + - APM agent configuration + parameters: + - $ref: '../components/headers/elastic_api_version.yaml' + - $ref: '../components/headers/kbn_xsrf.yaml' + requestBody: + required: true + content: + application/json: + schema: + $ref: '../components/schemas/search_agent_configuration_object.yaml' + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '../components/schemas/search_agent_configuration_response.yaml' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '../components/schemas/400_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/401_response.yaml' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '../components/schemas/404_response.yaml' diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@settings@agent_configuration@view.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@settings@agent_configuration@view.yaml new file mode 100644 index 0000000000000..23e5cc2186d12 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@settings@agent_configuration@view.yaml @@ -0,0 +1,44 @@ +get: + summary: Get single agent configuration + operationId: getSingleAgentConfiguration + tags: + - APM agent configuration + parameters: + - $ref: '../components/headers/elastic_api_version.yaml' + - name: name + in: query + description: Service name + schema: + type: string + example: node + - name: environment + in: query + description: Service environment + schema: + type: string + example: prod + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '../components/schemas/single_agent_configuration_response.yaml' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '../components/schemas/400_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/401_response.yaml' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '../components/schemas/404_response.yaml' diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@sourcemaps.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@sourcemaps.yaml new file mode 100644 index 0000000000000..142cd0d2673c1 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@sourcemaps.yaml @@ -0,0 +1,101 @@ +get: + summary: Get source maps + description: Returns an array of Fleet artifacts, including source map uploads. + operationId: getSourceMaps + tags: + - APM sourcemaps + parameters: + - $ref: '../components/headers/elastic_api_version.yaml' + - name: page + in: query + description: Page number + schema: + type: number + - name: perPage + in: query + description: Number of records per page + schema: + type: number + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '../components/schemas/source_maps_response.yaml' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '../components/schemas/400_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/401_response.yaml' + '500': + description: Internal Server Error response + content: + application/json: + schema: + $ref: '../components/schemas/500_response.yaml' + '501': + description: Not Implemented response + content: + application/json: + schema: + $ref: '../components/schemas/501_response.yaml' +post: + summary: Upload source map + description: Upload a source map for a specific service and version. + operationId: uploadSourceMap + tags: + - APM sourcemaps + parameters: + - $ref: '../components/headers/elastic_api_version.yaml' + - $ref: '../components/headers/kbn_xsrf.yaml' + requestBody: + required: true + content: + multipart/form-data: + schema: + $ref: '../components/schemas/upload_source_map_object.yaml' + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '../components/schemas/upload_source_maps_response.yaml' + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '../components/schemas/400_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/401_response.yaml' + '403': + description: Forbidden response + content: + application/json: + schema: + $ref: '../components/schemas/403_response.yaml' + '500': + description: Internal Server Error response + content: + application/json: + schema: + $ref: '../components/schemas/500_response.yaml' + '501': + description: Not Implemented response + content: + application/json: + schema: + $ref: '../components/schemas/501_response.yaml' diff --git a/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@sourcemaps@{id}.yaml b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@sourcemaps@{id}.yaml new file mode 100644 index 0000000000000..3f165360bf60c --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/docs/openapi/apm/paths/api@apm@sourcemaps@{id}.yaml @@ -0,0 +1,53 @@ +delete: + summary: Delete source map + description: Delete a previously uploaded source map. + operationId: deleteSourceMap + tags: + - APM sourcemaps + parameters: + - $ref: '../components/headers/elastic_api_version.yaml' + - $ref: '../components/headers/kbn_xsrf.yaml' + - name: id + in: path + description: Source map identifier + required: true + schema: + type: string + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + additionalProperties: false + '400': + description: Bad Request response + content: + application/json: + schema: + $ref: '../components/schemas/400_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/401_response.yaml' + '403': + description: Forbidden response + content: + application/json: + schema: + $ref: '../components/schemas/403_response.yaml' + '500': + description: Internal Server Error response + content: + application/json: + schema: + $ref: '../components/schemas/500_response.yaml' + '501': + description: Not Implemented response + content: + application/json: + schema: + $ref: '../components/schemas/501_response.yaml' diff --git a/x-pack/plugins/observability_solution/apm/ftr_e2e/README.md b/x-pack/plugins/observability_solution/apm/ftr_e2e/README.md index 8336c037ff21d..ecdb37a5f5229 100644 --- a/x-pack/plugins/observability_solution/apm/ftr_e2e/README.md +++ b/x-pack/plugins/observability_solution/apm/ftr_e2e/README.md @@ -1,6 +1,6 @@ # APM E2E -APM uses [FTR](../../../../../packages/kbn-test/README.md) (functional test runner) and [Cypress](https://www.cypress.io/) to run the e2e tests. The tests are located at `kibana/x-pack/plugins/observability_solution/apm/ftr_e2e/cypress/integration`. +APM uses [FTR](../../../../../packages/kbn-test/README.mdx) (functional test runner) and [Cypress](https://www.cypress.io/) to run the e2e tests. The tests are located at `kibana/x-pack/plugins/observability_solution/apm/ftr_e2e/cypress/integration`. ## Tips and best practices diff --git a/x-pack/plugins/observability_solution/apm/ftr_e2e/cypress/e2e/transaction_details/transaction_details.cy.ts b/x-pack/plugins/observability_solution/apm/ftr_e2e/cypress/e2e/transaction_details/transaction_details.cy.ts index 3ae431f5d3299..0fc1b609b14ba 100644 --- a/x-pack/plugins/observability_solution/apm/ftr_e2e/cypress/e2e/transaction_details/transaction_details.cy.ts +++ b/x-pack/plugins/observability_solution/apm/ftr_e2e/cypress/e2e/transaction_details/transaction_details.cy.ts @@ -16,7 +16,7 @@ const timeRange = { rangeTo: end, }; // flaky -describe.skip('Transaction details', () => { +describe('Transaction details', () => { before(() => { synthtrace.index( opbeans({ @@ -34,8 +34,7 @@ describe.skip('Transaction details', () => { cy.loginAsViewerUser(); }); - // skipping this as it´s been failing a lot lately, more information here https://github.com/elastic/kibana/issues/197386 - it.skip('shows transaction name and transaction charts', () => { + it('shows transaction name and transaction charts', () => { cy.intercept('GET', '/internal/apm/services/opbeans-java/transactions/charts/latency?*').as( 'transactionLatencyRequest' ); @@ -61,7 +60,7 @@ describe.skip('Transaction details', () => { '@transactionThroughputRequest', '@transactionFailureRateRequest', ], - { timeout: 60000 } + { timeout: 30000 } ).spread((latencyInterception, throughputInterception, failureRateInterception) => { expect(latencyInterception.request.query.transactionName).to.be.eql('GET /api/product'); @@ -107,8 +106,7 @@ describe.skip('Transaction details', () => { ); cy.contains('Create SLO'); }); - // skipping this as it´s been failing a lot lately, more information here https://github.com/elastic/kibana/issues/197386 - it.skip('shows top errors table', () => { + it('shows top errors table', () => { cy.visitKibana( `/app/apm/services/opbeans-java/transactions/view?${new URLSearchParams({ ...timeRange, @@ -116,7 +114,7 @@ describe.skip('Transaction details', () => { })}` ); - cy.contains('Top 5 errors'); + cy.contains('Top 5 errors', { timeout: 30000 }); cy.getByTestSubj('topErrorsForTransactionTable').contains('a', '[MockError] Foo').click(); cy.url().should('include', 'opbeans-java/errors'); }); diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/dependency_operation_detail_view/maybe_redirect_to_available_span_sample.ts b/x-pack/plugins/observability_solution/apm/public/components/app/dependency_operation_detail_view/maybe_redirect_to_available_span_sample.ts index 1815897cb7c7a..d47cdbca64b92 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/dependency_operation_detail_view/maybe_redirect_to_available_span_sample.ts +++ b/x-pack/plugins/observability_solution/apm/public/components/app/dependency_operation_detail_view/maybe_redirect_to_available_span_sample.ts @@ -24,7 +24,7 @@ export function maybeRedirectToAvailableSpanSample({ page: number; replace: typeof urlHelpersReplace; history: History; - samples: Array<{ spanId: string; traceId: string; transactionId: string }>; + samples: Array<{ spanId: string; traceId: string; transactionId?: string }>; }) { if (spanFetchStatus !== FETCH_STATUS.SUCCESS) { // we're still loading, don't do anything diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/metrics/static_dashboard/dashboards/dashboard_catalog.ts b/x-pack/plugins/observability_solution/apm/public/components/app/metrics/static_dashboard/dashboards/dashboard_catalog.ts index 2d3ea5fded80b..6f81ef6db535b 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/metrics/static_dashboard/dashboards/dashboard_catalog.ts +++ b/x-pack/plugins/observability_solution/apm/public/components/app/metrics/static_dashboard/dashboards/dashboard_catalog.ts @@ -12,6 +12,9 @@ export const AGENT_NAME_DASHBOARD_FILE_MAPPING: Record = { 'opentelemetry/java': 'opentelemetry_java', 'opentelemetry/java/opentelemetry-java-instrumentation': 'opentelemetry_java', 'opentelemetry/java/elastic': 'opentelemetry_java', + 'opentelemetry/dotnet': 'opentelemetry_dotnet', + 'opentelemetry/dotnet/opentelemetry-dotnet-instrumentation': 'opentelemetry_dotnet', + 'opentelemetry/dotnet/elastic': 'opentelemetry_dotnet', }; /** @@ -44,6 +47,12 @@ export async function loadDashboardFile(filename: string): Promise { './opentelemetry_java.json' ); } + case 'opentelemetry_dotnet': { + return import( + /* webpackChunkName: "lazyOtelDotnetDashboard" */ + './opentelemetry_dotnet.json' + ); + } default: { break; } diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/metrics/static_dashboard/dashboards/opentelemetry_dotnet.json b/x-pack/plugins/observability_solution/apm/public/components/app/metrics/static_dashboard/dashboards/opentelemetry_dotnet.json new file mode 100644 index 0000000000000..2862bf0a586d7 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/public/components/app/metrics/static_dashboard/dashboards/opentelemetry_dotnet.json @@ -0,0 +1 @@ +{"attributes":{"controlGroupInput":{"chainingSystem":"HIERARCHICAL","controlStyle":"oneLine","ignoreParentSettingsJSON":"{\"ignoreFilters\":false,\"ignoreQuery\":false,\"ignoreTimerange\":false,\"ignoreValidations\":false}","panelsJSON":"{\"2be66584-9de4-4a36-ba54-bfdd1b4ccfb4\":{\"type\":\"optionsListControl\",\"order\":0,\"grow\":true,\"width\":\"medium\",\"explicitInput\":{\"id\":\"2be66584-9de4-4a36-ba54-bfdd1b4ccfb4\",\"fieldName\":\"service.node.name\",\"title\":\"Instance\",\"grow\":true,\"width\":\"medium\",\"searchTechnique\":\"prefix\",\"selectedOptions\":[],\"existsSelected\":true,\"enhancements\":{}}}}"},"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"optionsJSON":"{\"useMargins\":true,\"syncColors\":false,\"syncCursor\":true,\"syncTooltips\":false,\"hidePanelTitles\":false}","panelsJSON":"[{\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":0,\"w\":48,\"h\":8,\"i\":\"ea9f86f0-ff73-4c92-9b93-41baebdcffab\"},\"panelIndex\":\"ea9f86f0-ff73-4c92-9b93-41baebdcffab\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"visualizationType\":\"lnsDatatable\",\"type\":\"lens\",\"references\":[{\"type\":\"index-pattern\",\"id\":\"APM_STATIC_DATA_VIEW_ID\",\"name\":\"indexpattern-datasource-layer-1ba88117-6e95-46e2-8667-e0bc15145182\"}],\"state\":{\"visualization\":{\"columns\":[{\"columnId\":\"d5553b2b-25e9-4b94-9697-f04885f4a067\",\"isTransposed\":false,\"isMetric\":false,\"alignment\":\"left\",\"summaryRow\":\"none\",\"width\":547.5714285714286},{\"columnId\":\"b3da070f-3463-4990-b257-40ac399bec87\",\"isTransposed\":false,\"isMetric\":true,\"colorMode\":\"none\",\"hidden\":false,\"alignment\":\"left\",\"summaryRow\":\"avg\",\"width\":135.73809523809527},{\"columnId\":\"a7f6a205-1e8f-4b64-b745-efe20c2c6545\",\"isTransposed\":false,\"isMetric\":true,\"alignment\":\"left\",\"summaryRow\":\"sum\",\"width\":143.93809523809523},{\"columnId\":\"2bbcd9e9-15ff-42c1-98a8-65a5b9b4e4c5\",\"isTransposed\":false,\"isMetric\":true,\"alignment\":\"left\",\"summaryRow\":\"sum\",\"width\":142.68809523809523},{\"columnId\":\"9f3d67e3-0513-468e-b9b2-6d8780dac3e0\",\"isTransposed\":false,\"isMetric\":true,\"alignment\":\"left\",\"summaryRow\":\"sum\",\"width\":163.18809523809523},{\"columnId\":\"21bdcb2d-cee4-4dd9-84cb-5031f073bf85\",\"isTransposed\":false,\"isMetric\":true,\"alignment\":\"left\",\"width\":183.68809523809523}],\"layerId\":\"1ba88117-6e95-46e2-8667-e0bc15145182\",\"layerType\":\"data\",\"paging\":{\"size\":10,\"enabled\":true}},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[],\"datasourceStates\":{\"formBased\":{\"layers\":{\"1ba88117-6e95-46e2-8667-e0bc15145182\":{\"columns\":{\"d5553b2b-25e9-4b94-9697-f04885f4a067\":{\"label\":\"Host + Service instance\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"host.name\",\"isBucketed\":true,\"params\":{\"size\":25,\"orderBy\":{\"type\":\"alphabetical\",\"fallback\":false},\"orderDirection\":\"asc\",\"otherBucket\":true,\"missingBucket\":false,\"parentFormat\":{\"id\":\"multi_terms\"},\"include\":[],\"exclude\":[],\"includeIsRegex\":false,\"excludeIsRegex\":false,\"secondaryFields\":[\"service.node.name\"]},\"customLabel\":true},\"b3da070f-3463-4990-b257-40ac399bec87\":{\"label\":\"Memory usage\",\"dataType\":\"number\",\"operationType\":\"last_value\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"process.memory.usage\",\"filter\":{\"query\":\"\\\"process.memory.usage\\\": *\",\"language\":\"kuery\"},\"params\":{\"sortField\":\"@timestamp\",\"format\":{\"id\":\"bytes\",\"params\":{\"decimals\":0}}},\"customLabel\":true},\"a7f6a205-1e8f-4b64-b745-efe20c2c6545\":{\"label\":\"Gen 0 collections\",\"dataType\":\"number\",\"operationType\":\"last_value\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"process.runtime.dotnet.gc.collections.count\",\"filter\":{\"query\":\"\\\"process.runtime.dotnet.gc.collections.count\\\": * AND labels.generation : \\\"gen0\\\" \",\"language\":\"kuery\"},\"params\":{\"sortField\":\"@timestamp\"},\"customLabel\":true},\"2bbcd9e9-15ff-42c1-98a8-65a5b9b4e4c5\":{\"label\":\"Gen 1 collections\",\"dataType\":\"number\",\"operationType\":\"last_value\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"process.runtime.dotnet.gc.collections.count\",\"filter\":{\"query\":\"\\\"process.runtime.dotnet.gc.collections.count\\\": * AND labels.generation : \\\"gen1\\\" \",\"language\":\"kuery\"},\"params\":{\"sortField\":\"@timestamp\"},\"customLabel\":true},\"9f3d67e3-0513-468e-b9b2-6d8780dac3e0\":{\"label\":\"Gen 2 collections\",\"dataType\":\"number\",\"operationType\":\"last_value\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"process.runtime.dotnet.gc.collections.count\",\"filter\":{\"query\":\"\\\"process.runtime.dotnet.gc.collections.count\\\": * AND labels.generation : \\\"gen2\\\" \",\"language\":\"kuery\"},\"params\":{\"sortField\":\"@timestamp\"},\"customLabel\":true},\"21bdcb2d-cee4-4dd9-84cb-5031f073bf85\":{\"label\":\"Managed threads\",\"dataType\":\"number\",\"operationType\":\"last_value\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"process.runtime.dotnet.thread_pool.threads.count\",\"filter\":{\"query\":\"\\\"process.runtime.dotnet.thread_pool.threads.count\\\": *\",\"language\":\"kuery\"},\"params\":{\"sortField\":\"@timestamp\"},\"customLabel\":true}},\"columnOrder\":[\"d5553b2b-25e9-4b94-9697-f04885f4a067\",\"21bdcb2d-cee4-4dd9-84cb-5031f073bf85\",\"b3da070f-3463-4990-b257-40ac399bec87\",\"a7f6a205-1e8f-4b64-b745-efe20c2c6545\",\"2bbcd9e9-15ff-42c1-98a8-65a5b9b4e4c5\",\"9f3d67e3-0513-468e-b9b2-6d8780dac3e0\"],\"sampling\":1,\"ignoreGlobalFilters\":false,\"incompleteColumns\":{},\"indexPatternId\":\"apm_static_data_view_id_default\"}},\"currentIndexPatternId\":\"apm_static_data_view_id_default\"},\"indexpattern\":{\"layers\":{}},\"textBased\":{\"layers\":{}}},\"internalReferences\":[],\"adHocDataViews\":{}}},\"hidePanelTitles\":true,\"enhancements\":{}},\"title\":\"\"},{\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":8,\"w\":25,\"h\":16,\"i\":\"d0991248-2fad-4f28-bedc-b8723bc45a81\"},\"panelIndex\":\"d0991248-2fad-4f28-bedc-b8723bc45a81\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"description\":\"The amount of physical memory allocated for this process.\",\"visualizationType\":\"lnsXY\",\"type\":\"lens\",\"references\":[{\"type\":\"index-pattern\",\"id\":\"APM_STATIC_DATA_VIEW_ID\",\"name\":\"indexpattern-datasource-layer-961b1efd-6f0d-41e4-a72b-5d66237d212b\"}],\"state\":{\"visualization\":{\"legend\":{\"isVisible\":true,\"position\":\"bottom\",\"showSingleSeries\":true},\"valueLabels\":\"hide\",\"fittingFunction\":\"None\",\"yTitle\":\"Allocated physical memory\",\"axisTitlesVisibilitySettings\":{\"x\":false,\"yLeft\":false,\"yRight\":true},\"tickLabelsVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"labelsOrientation\":{\"x\":0,\"yLeft\":0,\"yRight\":0},\"gridlinesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"preferredSeriesType\":\"bar_stacked\",\"layers\":[{\"layerId\":\"961b1efd-6f0d-41e4-a72b-5d66237d212b\",\"accessors\":[\"81968f1a-1c2f-46cb-8276-3dca900342e9\",\"54c0cda3-76f3-4b75-9fe1-2265fa68993c\"],\"position\":\"top\",\"seriesType\":\"line\",\"showGridlines\":false,\"layerType\":\"data\",\"colorMapping\":{\"assignments\":[],\"specialAssignments\":[{\"rule\":{\"type\":\"other\"},\"color\":{\"type\":\"loop\"},\"touched\":false}],\"paletteId\":\"eui_amsterdam_color_blind\",\"colorMode\":{\"type\":\"categorical\"}},\"xAccessor\":\"2405efa8-e18d-426f-822f-3a4551bf97d2\",\"yConfig\":[]}]},\"query\":{\"query\":\"agent.name: \\\"opentelemetry/dotnet\\\" \",\"language\":\"kuery\"},\"filters\":[],\"datasourceStates\":{\"formBased\":{\"layers\":{\"961b1efd-6f0d-41e4-a72b-5d66237d212b\":{\"columns\":{\"2405efa8-e18d-426f-822f-3a4551bf97d2\":{\"label\":\"@timestamp\",\"dataType\":\"date\",\"operationType\":\"date_histogram\",\"sourceField\":\"@timestamp\",\"isBucketed\":true,\"scale\":\"interval\",\"params\":{\"interval\":\"auto\",\"includeEmptyRows\":true,\"dropPartials\":false}},\"81968f1a-1c2f-46cb-8276-3dca900342e9\":{\"label\":\"Average\",\"dataType\":\"number\",\"operationType\":\"average\",\"sourceField\":\"process.memory.usage\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"format\":{\"id\":\"bytes\",\"params\":{\"decimals\":0}},\"emptyAsNull\":true},\"customLabel\":true},\"54c0cda3-76f3-4b75-9fe1-2265fa68993c\":{\"label\":\"Max\",\"dataType\":\"number\",\"operationType\":\"max\",\"sourceField\":\"process.memory.usage\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"emptyAsNull\":true,\"format\":{\"id\":\"bytes\",\"params\":{\"decimals\":0}}},\"customLabel\":true}},\"columnOrder\":[\"2405efa8-e18d-426f-822f-3a4551bf97d2\",\"81968f1a-1c2f-46cb-8276-3dca900342e9\",\"54c0cda3-76f3-4b75-9fe1-2265fa68993c\"],\"incompleteColumns\":{},\"sampling\":1,\"indexPatternId\":\"apm_static_data_view_id_default\"}},\"currentIndexPatternId\":\"apm_static_data_view_id_default\"},\"indexpattern\":{\"layers\":{}},\"textBased\":{\"layers\":{}}},\"internalReferences\":[],\"adHocDataViews\":{}}},\"description\":\"The amount of physical memory allocated to the .NET process.\",\"enhancements\":{}},\"title\":\"Allocated physical memory\"},{\"type\":\"lens\",\"gridData\":{\"x\":25,\"y\":8,\"w\":23,\"h\":16,\"i\":\"0bf63f9e-8797-4249-85f7-9407c165f732\"},\"panelIndex\":\"0bf63f9e-8797-4249-85f7-9407c165f732\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"visualizationType\":\"lnsXY\",\"type\":\"lens\",\"references\":[{\"type\":\"index-pattern\",\"id\":\"APM_STATIC_DATA_VIEW_ID\",\"name\":\"indexpattern-datasource-layer-eb4e02de-8962-40fa-9e75-ff25862ca5f3\"}],\"state\":{\"visualization\":{\"legend\":{\"isVisible\":true,\"position\":\"right\",\"maxLines\":1},\"valueLabels\":\"hide\",\"fittingFunction\":\"None\",\"axisTitlesVisibilitySettings\":{\"x\":false,\"yLeft\":false,\"yRight\":true},\"tickLabelsVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"labelsOrientation\":{\"x\":0,\"yLeft\":0,\"yRight\":0},\"gridlinesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"preferredSeriesType\":\"line\",\"layers\":[{\"layerId\":\"eb4e02de-8962-40fa-9e75-ff25862ca5f3\",\"accessors\":[\"cced3ff5-cfa3-4804-93be-c8d893114e93\",\"211c2cbb-033a-454b-b379-186b8d7b247e\",\"5ac14ba1-f6d4-4015-96d1-aeaa3ed63aec\",\"938252b1-14ec-4a64-b3e5-108e096f116b\",\"1e106c40-c845-4368-baaa-4057e1d29d92\"],\"position\":\"top\",\"seriesType\":\"line\",\"showGridlines\":false,\"layerType\":\"data\",\"colorMapping\":{\"assignments\":[],\"specialAssignments\":[{\"rule\":{\"type\":\"other\"},\"color\":{\"type\":\"loop\"},\"touched\":false}],\"paletteId\":\"eui_amsterdam_color_blind\",\"colorMode\":{\"type\":\"categorical\"}},\"xAccessor\":\"cb7bae9c-fdc5-44a8-8ee8-c0762595511c\"}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[],\"datasourceStates\":{\"formBased\":{\"layers\":{\"eb4e02de-8962-40fa-9e75-ff25862ca5f3\":{\"columns\":{\"cb7bae9c-fdc5-44a8-8ee8-c0762595511c\":{\"label\":\"@timestamp\",\"dataType\":\"date\",\"operationType\":\"date_histogram\",\"sourceField\":\"@timestamp\",\"isBucketed\":true,\"scale\":\"interval\",\"params\":{\"interval\":\"auto\",\"includeEmptyRows\":true,\"dropPartials\":false}},\"cced3ff5-cfa3-4804-93be-c8d893114e93\":{\"label\":\"Gen 0\",\"dataType\":\"number\",\"operationType\":\"average\",\"sourceField\":\"process.runtime.dotnet.gc.heap.size\",\"isBucketed\":false,\"scale\":\"ratio\",\"filter\":{\"query\":\"labels.generation : \\\"gen0\\\" \",\"language\":\"kuery\"},\"params\":{\"format\":{\"id\":\"bytes\",\"params\":{\"decimals\":2}},\"emptyAsNull\":true},\"customLabel\":true},\"211c2cbb-033a-454b-b379-186b8d7b247e\":{\"label\":\"Gen 1\",\"dataType\":\"number\",\"operationType\":\"average\",\"sourceField\":\"process.runtime.dotnet.gc.heap.size\",\"isBucketed\":false,\"scale\":\"ratio\",\"filter\":{\"query\":\"labels.generation : \\\"gen1\\\" \",\"language\":\"kuery\"},\"params\":{\"emptyAsNull\":true,\"format\":{\"id\":\"bytes\",\"params\":{\"decimals\":2}}},\"customLabel\":true},\"5ac14ba1-f6d4-4015-96d1-aeaa3ed63aec\":{\"label\":\"Gen 2\",\"dataType\":\"number\",\"operationType\":\"average\",\"sourceField\":\"process.runtime.dotnet.gc.heap.size\",\"isBucketed\":false,\"scale\":\"ratio\",\"filter\":{\"query\":\"labels.generation : \\\"gen2\\\" \",\"language\":\"kuery\"},\"params\":{\"emptyAsNull\":true,\"format\":{\"id\":\"bytes\",\"params\":{\"decimals\":2}}},\"customLabel\":true},\"938252b1-14ec-4a64-b3e5-108e096f116b\":{\"label\":\"LOH\",\"dataType\":\"number\",\"operationType\":\"average\",\"sourceField\":\"process.runtime.dotnet.gc.heap.size\",\"isBucketed\":false,\"scale\":\"ratio\",\"filter\":{\"query\":\"labels.generation:\\\"loh\\\" \",\"language\":\"kuery\"},\"params\":{\"emptyAsNull\":true,\"format\":{\"id\":\"bytes\",\"params\":{\"decimals\":2}}},\"customLabel\":true},\"1e106c40-c845-4368-baaa-4057e1d29d92\":{\"label\":\"POH\",\"dataType\":\"number\",\"operationType\":\"median\",\"sourceField\":\"process.runtime.dotnet.gc.heap.size\",\"isBucketed\":false,\"scale\":\"ratio\",\"filter\":{\"query\":\"labels.generation : \\\"poh\\\" \",\"language\":\"kuery\"},\"params\":{\"emptyAsNull\":true,\"format\":{\"id\":\"bytes\",\"params\":{\"decimals\":2}}},\"customLabel\":true}},\"columnOrder\":[\"cb7bae9c-fdc5-44a8-8ee8-c0762595511c\",\"cced3ff5-cfa3-4804-93be-c8d893114e93\",\"211c2cbb-033a-454b-b379-186b8d7b247e\",\"5ac14ba1-f6d4-4015-96d1-aeaa3ed63aec\",\"938252b1-14ec-4a64-b3e5-108e096f116b\",\"1e106c40-c845-4368-baaa-4057e1d29d92\"],\"incompleteColumns\":{},\"sampling\":1,\"indexPatternId\":\"apm_static_data_view_id_default\"}},\"currentIndexPatternId\":\"apm_static_data_view_id_default\"},\"indexpattern\":{\"layers\":{}},\"textBased\":{\"layers\":{}}},\"internalReferences\":[],\"adHocDataViews\":{}}},\"enhancements\":{}},\"title\":\"Average GC heap size by generation\"}]","timeRestore":false,"title":".NET OpenTelemetry Runtime Metrics","version":1},"coreMigrationVersion":"8.8.0","created_at":"2024-05-17T13:46:01.942Z","id":"c65be603-2c73-4417-972c-033586a56102","managed":false,"references":[{"id":"apm_static_data_view_id_default","name":"ea9f86f0-ff73-4c92-9b93-41baebdcffab:indexpattern-datasource-layer-1ba88117-6e95-46e2-8667-e0bc15145182","type":"index-pattern"},{"id":"APM_STATIC_DATA_VIEW_ID","name":"d0991248-2fad-4f28-bedc-b8723bc45a81:indexpattern-datasource-layer-961b1efd-6f0d-41e4-a72b-5d66237d212b","type":"index-pattern"},{"id":"APM_STATIC_DATA_VIEW_ID","name":"0bf63f9e-8797-4249-85f7-9407c165f732:indexpattern-datasource-layer-eb4e02de-8962-40fa-9e75-ff25862ca5f3","type":"index-pattern"},{"id":"APM_STATIC_DATA_VIEW_ID","name":"controlGroup_2be66584-9de4-4a36-ba54-bfdd1b4ccfb4:optionsListDataView","type":"index-pattern"}],"type":"dashboard","typeMigrationVersion":"8.9.0","updated_at":"2024-05-17T13:46:01.942Z","version":"WzM0NTMsN10="} \ No newline at end of file diff --git a/x-pack/plugins/observability_solution/apm/public/components/routing/templates/apm_service_template/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/routing/templates/apm_service_template/index.tsx index 0e095694cd538..6c2fdaea96687 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/routing/templates/apm_service_template/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/routing/templates/apm_service_template/index.tsx @@ -8,7 +8,6 @@ import { EuiFlexGroup, EuiFlexItem, EuiLoadingLogo, EuiSpacer, EuiTitle } from '@elastic/eui'; import React from 'react'; import { useHistory, useLocation } from 'react-router-dom'; -import { isLogsOnlySignal } from '../../../../utils/get_signal_type'; import { isMobileAgentName } from '../../../../../common/agent_name'; import { ApmServiceContextProvider } from '../../../../context/apm_service/apm_service_context'; import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; @@ -55,7 +54,7 @@ function TemplateWithContext({ title, children, selectedTab, searchBarOptions }: const tabs = useTabs({ selectedTab }); - const { agentName, serviceAgentStatus, serviceEntitySummary } = useApmServiceContext(); + const { agentName, serviceAgentStatus } = useApmServiceContext(); const isPendingServiceAgent = !agentName && isPending(serviceAgentStatus); @@ -76,9 +75,6 @@ function TemplateWithContext({ title, children, selectedTab, searchBarOptions }: }); } - const hasLogsOnlySignal = - serviceEntitySummary?.dataStreamTypes && isLogsOnlySignal(serviceEntitySummary.dataStreamTypes); - return ( ) : ( <> - {!hasLogsOnlySignal && } + {children} diff --git a/x-pack/plugins/observability_solution/apm/public/context/breadcrumbs/context.tsx b/x-pack/plugins/observability_solution/apm/public/context/breadcrumbs/context.tsx index e661ef2450310..7ec17b3a6cf3b 100644 --- a/x-pack/plugins/observability_solution/apm/public/context/breadcrumbs/context.tsx +++ b/x-pack/plugins/observability_solution/apm/public/context/breadcrumbs/context.tsx @@ -76,7 +76,7 @@ export function BreadcrumbsContextProvider({ children }: { children: React.React }; }); - useBreadcrumbs(formattedBreadcrumbs, { serverless }); + useBreadcrumbs(formattedBreadcrumbs, { serverless, absoluteProjectStyleBreadcrumbs: false }); return {children}; } diff --git a/x-pack/plugins/observability_solution/apm/public/context/breadcrumbs/use_breadcrumb.ts b/x-pack/plugins/observability_solution/apm/public/context/breadcrumbs/use_breadcrumb.ts index a6b80fd088bff..846aa1ec70877 100644 --- a/x-pack/plugins/observability_solution/apm/public/context/breadcrumbs/use_breadcrumb.ts +++ b/x-pack/plugins/observability_solution/apm/public/context/breadcrumbs/use_breadcrumb.ts @@ -8,8 +8,10 @@ import { useCurrentRoute } from '@kbn/typed-react-router-config'; import { useContext, useEffect, useRef } from 'react'; import { castArray } from 'lodash'; +import useObservable from 'react-use/lib/useObservable'; import { Breadcrumb, BreadcrumbsContext } from './context'; import { useKibanaEnvironmentContext } from '../kibana_environment_context/use_kibana_environment_context'; +import { useKibana } from '../kibana_context/use_kibana'; export function useBreadcrumb( callback: () => Breadcrumb | Breadcrumb[], @@ -17,6 +19,9 @@ export function useBreadcrumb( options?: { omitRootOnServerless?: boolean; omitOnServerless?: boolean } ) { const { isServerlessEnv } = useKibanaEnvironmentContext(); + const { + services: { chrome }, + } = useKibana(); const { omitRootOnServerless = false, omitOnServerless = false } = options || {}; const api = useContext(BreadcrumbsContext); @@ -29,8 +34,11 @@ export function useBreadcrumb( const matchedRoute = useRef(match?.route); + const chromeStyle = useObservable(chrome.getChromeStyle$()); + useEffect(() => { - if (isServerlessEnv && omitOnServerless) { + const isProjectStyle = isServerlessEnv || chromeStyle === 'project'; + if (isProjectStyle && omitOnServerless) { return; } @@ -42,10 +50,9 @@ export function useBreadcrumb( if (matchedRoute.current) { const breadcrumbs = castArray(callback()); - api.set( matchedRoute.current, - isServerlessEnv && omitRootOnServerless && breadcrumbs.length >= 1 + isProjectStyle && omitRootOnServerless && breadcrumbs.length >= 1 ? breadcrumbs.slice(1) : breadcrumbs ); @@ -57,5 +64,5 @@ export function useBreadcrumb( } }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [match, ...fnDeps]); + }, [match, chromeStyle, ...fnDeps]); } diff --git a/x-pack/plugins/observability_solution/apm/server/routes/dependencies/get_top_dependency_spans.ts b/x-pack/plugins/observability_solution/apm/server/routes/dependencies/get_top_dependency_spans.ts index 2a5a804d57f04..6066ebda155d5 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/dependencies/get_top_dependency_spans.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/dependencies/get_top_dependency_spans.ts @@ -41,7 +41,7 @@ export interface DependencySpan { serviceName: string; agentName: AgentName; traceId: string; - transactionId: string; + transactionId?: string; transactionType?: string; transactionName?: string; duration: number; @@ -72,7 +72,6 @@ export async function getTopDependencySpans({ const topDedsRequiredFields = asMutableArray([ SPAN_ID, TRACE_ID, - TRANSACTION_ID, SPAN_NAME, SERVICE_NAME, SERVICE_ENVIRONMENT, @@ -98,7 +97,6 @@ export async function getTopDependencySpans({ ...kqlQuery(kuery), ...termQuery(SPAN_DESTINATION_SERVICE_RESOURCE, dependencyName), ...termQuery(SPAN_NAME, spanName), - { exists: { field: TRANSACTION_ID } }, ...((sampleRangeFrom ?? 0) >= 0 && (sampleRangeTo ?? 0) > 0 ? [ { @@ -119,9 +117,10 @@ export async function getTopDependencySpans({ }) ).hits.hits.map((hit) => unflattenKnownApmEventFields(hit.fields, topDedsRequiredFields)); - const transactionIds = spans.map((span) => span.transaction.id); + const traceIds = spans.map((span) => span.trace.id); const txRequiredFields = asMutableArray([ + TRACE_ID, TRANSACTION_ID, TRANSACTION_TYPE, TRANSACTION_NAME, @@ -134,10 +133,10 @@ export async function getTopDependencySpans({ }, body: { track_total_hits: false, - size: transactionIds.length, + size: traceIds.length, query: { bool: { - filter: [...termsQuery(TRANSACTION_ID, ...transactionIds)], + filter: [...termsQuery(TRACE_ID, ...traceIds), { exists: { field: TRANSACTION_ID } }], }, }, fields: txRequiredFields, @@ -148,10 +147,10 @@ export async function getTopDependencySpans({ }) ).hits.hits.map((hit) => unflattenKnownApmEventFields(hit.fields, txRequiredFields)); - const transactionsById = keyBy(transactions, (transaction) => transaction.transaction.id); + const transactionsByTraceId = keyBy(transactions, (transaction) => transaction.trace.id); return spans.map((span): DependencySpan => { - const transaction = maybe(transactionsById[span.transaction!.id]); + const transaction = maybe(transactionsByTraceId[span.trace!.id]); return { '@timestamp': new Date(span['@timestamp']).getTime(), @@ -162,7 +161,7 @@ export async function getTopDependencySpans({ duration: span.span.duration.us, traceId: span.trace.id, outcome: (span.event?.outcome || EventOutcome.unknown) as EventOutcome, - transactionId: span.transaction!.id, + transactionId: transaction?.transaction.id, transactionType: transaction?.transaction.type, transactionName: transaction?.transaction.name, }; diff --git a/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/embeddable/embeddable.tsx b/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/embeddable/embeddable.tsx index a0079568803b6..a7760014dec8c 100644 --- a/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/embeddable/embeddable.tsx +++ b/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/embeddable/embeddable.tsx @@ -288,5 +288,8 @@ const Wrapper = styled.div<{ right: 50%; transform: translate(50%, -50%); } + .embPanel { + outline: none; + } } `; diff --git a/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/index.tsx b/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/index.tsx index c4061f05ce91b..750429602aecf 100644 --- a/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/index.tsx +++ b/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/index.tsx @@ -60,7 +60,7 @@ export function ExploratoryViewPage({ }), }, ], - { app } + { app, classicOnly: true } ); const kbnUrlStateStorage = useSessionStorage diff --git a/x-pack/plugins/observability_solution/infra/public/apps/logs_app.tsx b/x-pack/plugins/observability_solution/infra/public/apps/logs_app.tsx index 329e059288e3e..51749d8095481 100644 --- a/x-pack/plugins/observability_solution/infra/public/apps/logs_app.tsx +++ b/x-pack/plugins/observability_solution/infra/public/apps/logs_app.tsx @@ -24,6 +24,7 @@ export const renderApp = ( core: CoreStart, plugins: InfraClientStartDeps, pluginStart: InfraClientStartExports, + isLogsExplorerAccessible: boolean, { element, history, setHeaderActionMenu, theme$ }: AppMountParameters ) => { const storage = new Storage(window.localStorage); @@ -39,6 +40,7 @@ export const renderApp = ( pluginStart={pluginStart} setHeaderActionMenu={setHeaderActionMenu} theme$={theme$} + isLogsExplorerAccessible={isLogsExplorerAccessible} />, element ); @@ -56,8 +58,18 @@ const LogsApp: React.FC<{ setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; storage: Storage; theme$: AppMountParameters['theme$']; -}> = ({ core, history, pluginStart, plugins, setHeaderActionMenu, storage, theme$ }) => { - const { logs, discover, fleet } = core.application.capabilities; + isLogsExplorerAccessible: boolean; +}> = ({ + core, + history, + pluginStart, + plugins, + setHeaderActionMenu, + storage, + theme$, + isLogsExplorerAccessible, +}) => { + const { logs } = core.application.capabilities; return ( @@ -74,7 +86,7 @@ const LogsApp: React.FC<{ toastsService={core.notifications.toasts} > - {Boolean(discover?.show && fleet?.read) && ( + {isLogsExplorerAccessible && ( { const { request$ } = useRequestObservable(); const { isActiveTab } = useTabSwitcherContext(); const { dataStreams, status: dataStreamsStatus } = useEntitySummary({ - entityType: EntityType.HOST, + entityType: ENTITY_TYPES.HOST, entityId: asset.name, }); const addMetricsCalloutId: AddMetricsCalloutKey = 'hostProcesses'; diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/template/page.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/template/page.tsx index c6e8790eeff6a..5c187bb6186d6 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/template/page.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/template/page.tsx @@ -50,18 +50,15 @@ export const Page = ({ tabs = [], links = [] }: ContentTemplateProps) => { const parentBreadcrumbResolver = useParentBreadcrumbResolver(); const breadcrumbOptions = parentBreadcrumbResolver.getBreadcrumbOptions(asset.type); - useMetricsBreadcrumbs( - [ - { - ...breadcrumbOptions.link, - text: breadcrumbOptions.text, - }, - { - text: asset.name, - }, - ], - { deeperContextServerless: true } - ); + useMetricsBreadcrumbs([ + { + ...breadcrumbOptions.link, + text: breadcrumbOptions.text, + }, + { + text: asset.name, + }, + ]); useEffect(() => { if (trackOnlyOnce.current) { diff --git a/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx b/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx index 63107f4a4d031..21e61c08d281b 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx @@ -9,13 +9,17 @@ import { EuiCallOut } from '@elastic/eui'; import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButton } from '@elastic/eui'; -import { AllDatasetsLocatorParams, ALL_DATASETS_LOCATOR_ID } from '@kbn/deeplinks-observability'; +import { + AllDatasetsLocatorParams, + ALL_DATASETS_LOCATOR_ID, + DatasetLocatorParams, +} from '@kbn/deeplinks-observability'; import { getRouterLinkProps } from '@kbn/router-utils'; import useLocalStorage from 'react-use/lib/useLocalStorage'; import { euiThemeVars } from '@kbn/ui-theme'; import { css } from '@emotion/css'; -import { SharePublicStart } from '@kbn/share-plugin/public/plugin'; +import { LocatorPublic } from '@kbn/share-plugin/common'; import { useKibanaContextForPlugin } from '../hooks/use_kibana'; const pageConfigurations = { @@ -44,14 +48,22 @@ interface LogsDeprecationCalloutProps { export const LogsDeprecationCallout = ({ page }: LogsDeprecationCalloutProps) => { const { - services: { share }, + services: { + share, + application: { + capabilities: { discover, fleet }, + }, + }, } = useKibanaContextForPlugin(); const { dismissalStorageKey, message } = pageConfigurations[page]; const [isDismissed, setDismissed] = useLocalStorage(dismissalStorageKey, false); - if (isDismissed) { + const allDatasetLocator = + share.url.locators.get(ALL_DATASETS_LOCATOR_ID); + + if (isDismissed || !(allDatasetLocator && discover?.show && fleet?.read)) { return null; } @@ -71,7 +83,7 @@ export const LogsDeprecationCallout = ({ page }: LogsDeprecationCalloutProps) => fill data-test-subj="infraLogsDeprecationCalloutTryLogsExplorerButton" color="warning" - {...getLogsExplorerLinkProps(share)} + {...getLogsExplorerLinkProps(allDatasetLocator)} > {i18n.translate('xpack.infra.logsDeprecationCallout.tryLogsExplorerButtonLabel', { defaultMessage: 'Try Logs Explorer', @@ -81,9 +93,7 @@ export const LogsDeprecationCallout = ({ page }: LogsDeprecationCalloutProps) => ); }; -const getLogsExplorerLinkProps = (share: SharePublicStart) => { - const locator = share.url.locators.get(ALL_DATASETS_LOCATOR_ID)!; - +const getLogsExplorerLinkProps = (locator: LocatorPublic) => { return getRouterLinkProps({ href: locator.getRedirectUrl({}), onClick: () => locator.navigate({}), diff --git a/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/job_setup_screen.tsx b/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/job_setup_screen.tsx index 9543e84585c02..e2ee8a127a881 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/job_setup_screen.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/job_setup_screen.tsx @@ -38,7 +38,7 @@ import { FixedDatePicker } from '../../fixed_datepicker'; import { DEFAULT_K8S_PARTITION_FIELD } from '../../../containers/ml/modules/metrics_k8s/module_descriptor'; import { convertKueryToElasticSearchQuery } from '../../../utils/kuery'; import { INFRA_ML_FLYOUT_FEEDBACK_LINK } from './flyout_home'; -import { KibanaEnvironmentContext } from '../../../hooks/use_kibana'; +import { KibanaEnvironmentContext, useKibanaContextForPlugin } from '../../../hooks/use_kibana'; import { MetricsExplorerKueryBar } from '../../../pages/metrics/metrics_explorer/components/kuery_bar'; interface Props { @@ -60,6 +60,7 @@ export const JobSetupScreen = (props: Props) => { const trackMetric = useUiTracker({ app: 'infra_metrics' }); const { kibanaVersion, isCloudEnv, isServerlessEnv } = useContext(KibanaEnvironmentContext); const { euiTheme } = useEuiTheme(); + const { telemetry } = useKibanaContextForPlugin().services; const indices = host.sourceConfiguration.indices; @@ -95,23 +96,47 @@ export const JobSetupScreen = (props: Props) => { } }, [props.jobType, kubernetes.jobSummaries, host.jobSummaries]); - const updateStart = useCallback((date: Moment) => { - setStartDate(date); - }, []); + const updateStart = useCallback( + (date: Moment) => { + setStartDate(date); + telemetry.reportAnomalyDetectionDateFieldChange({ + job_type: props.jobType, + start_date: date.toISOString(), + }); + }, + [telemetry, props.jobType] + ); const createJobs = useCallback(() => { + const date = moment(startDate).toDate(); if (hasSummaries) { + telemetry.reportAnomalyDetectionSetup({ + job_type: props.jobType, + configured_fields: { + start_date: date.toISOString(), + partition_field: partitionField ? partitionField[0] : undefined, + filter_field: filter ? filter : undefined, + }, + }); cleanUpAndSetUpModule( indices, - moment(startDate).toDate().getTime(), + date.getTime(), undefined, filterQuery, partitionField ? partitionField[0] : undefined ); } else { + telemetry.reportAnomalyDetectionSetup({ + job_type: props.jobType, + configured_fields: { + start_date: date.toISOString(), + partition_field: partitionField ? partitionField[0] : undefined, + filter_field: filter, + }, + }); setUpModule( indices, - moment(startDate).toDate().getTime(), + date.getTime(), undefined, filterQuery, partitionField ? partitionField[0] : undefined @@ -125,22 +150,36 @@ export const JobSetupScreen = (props: Props) => { indices, partitionField, startDate, + telemetry, + filter, + props.jobType, ]); const onFilterChange = useCallback( (f: string) => { setFilter(f || ''); setFilterQuery(convertKueryToElasticSearchQuery(f, metricsView?.dataViewReference) || ''); + telemetry.reportAnomalyDetectionFilterFieldChange({ + job_type: props.jobType, + filter_field: f ? f : undefined, + }); }, - [metricsView?.dataViewReference] + [metricsView?.dataViewReference, telemetry, props.jobType] ); /* eslint-disable-next-line react-hooks/exhaustive-deps */ const debouncedOnFilterChange = useCallback(debounce(onFilterChange, 500), [onFilterChange]); - const onPartitionFieldChange = useCallback((value: Array<{ label: string }>) => { - setPartitionField(value.map((v) => v.label)); - }, []); + const onPartitionFieldChange = useCallback( + (value: Array<{ label: string }>) => { + setPartitionField(value.map((v) => v.label)); + telemetry.reportAnomalyDetectionPartitionFieldChange({ + job_type: props.jobType, + partition_field: value.length > 0 ? value[0].label : undefined, + }); + }, + [telemetry, props.jobType] + ); useEffect(() => { if (props.jobType === 'kubernetes') { diff --git a/x-pack/plugins/observability_solution/infra/public/hooks/use_breadcrumbs.ts b/x-pack/plugins/observability_solution/infra/public/hooks/use_breadcrumbs.ts deleted file mode 100644 index 0cf9efef908bb..0000000000000 --- a/x-pack/plugins/observability_solution/infra/public/hooks/use_breadcrumbs.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ChromeBreadcrumb } from '@kbn/core/public'; -import { useEffect } from 'react'; -import { useLinkProps } from '@kbn/observability-shared-plugin/public'; -import { observabilityTitle } from '../translations'; -import { useKibanaContextForPlugin } from './use_kibana'; - -type AppId = 'logs' | 'metrics'; - -export const useBreadcrumbs = (app: AppId, appTitle: string, extraCrumbs: ChromeBreadcrumb[]) => { - const { - services: { chrome }, - } = useKibanaContextForPlugin(); - - const observabilityLinkProps = useLinkProps({ app: 'observability-overview' }); - const appLinkProps = useLinkProps({ app }); - - useEffect(() => { - const breadcrumbs = [ - { - ...observabilityLinkProps, - text: observabilityTitle, - }, - { - ...appLinkProps, - text: appTitle, - }, - ...extraCrumbs, - ]; - - const docTitle = [...breadcrumbs].reverse().map((breadcrumb) => breadcrumb.text as string); - - chrome.docTitle.change(docTitle); - chrome.setBreadcrumbs(breadcrumbs); - }, [appLinkProps, appTitle, chrome, extraCrumbs, observabilityLinkProps]); -}; diff --git a/x-pack/plugins/observability_solution/infra/public/hooks/use_logs_breadcrumbs.tsx b/x-pack/plugins/observability_solution/infra/public/hooks/use_logs_breadcrumbs.tsx index d2fc556caa57b..2eba7845b8d24 100644 --- a/x-pack/plugins/observability_solution/infra/public/hooks/use_logs_breadcrumbs.tsx +++ b/x-pack/plugins/observability_solution/infra/public/hooks/use_logs_breadcrumbs.tsx @@ -6,10 +6,18 @@ */ import { ChromeBreadcrumb } from '@kbn/core/public'; -import { useBreadcrumbs } from './use_breadcrumbs'; +import { useBreadcrumbs, useLinkProps } from '@kbn/observability-shared-plugin/public'; import { LOGS_APP } from '../../common/constants'; import { logsTitle } from '../translations'; export const useLogsBreadcrumbs = (extraCrumbs: ChromeBreadcrumb[]) => { - useBreadcrumbs(LOGS_APP, logsTitle, extraCrumbs); + const appLinkProps = useLinkProps({ app: LOGS_APP }); + const breadcrumbs = [ + { + ...appLinkProps, + text: logsTitle, + }, + ...extraCrumbs, + ]; + useBreadcrumbs(breadcrumbs, { classicOnly: true }); }; diff --git a/x-pack/plugins/observability_solution/infra/public/hooks/use_metrics_breadcrumbs.tsx b/x-pack/plugins/observability_solution/infra/public/hooks/use_metrics_breadcrumbs.tsx index defc8b3210f48..d5a6011a68e8e 100644 --- a/x-pack/plugins/observability_solution/infra/public/hooks/use_metrics_breadcrumbs.tsx +++ b/x-pack/plugins/observability_solution/infra/public/hooks/use_metrics_breadcrumbs.tsx @@ -5,42 +5,25 @@ * 2.0. */ -import { useEffect, useMemo } from 'react'; import { ChromeBreadcrumb } from '@kbn/core/public'; import { useBreadcrumbs, useLinkProps } from '@kbn/observability-shared-plugin/public'; import { METRICS_APP } from '../../common/constants'; import { metricsTitle } from '../translations'; import { useKibanaContextForPlugin } from './use_kibana'; -export const useMetricsBreadcrumbs = ( - extraCrumbs: ChromeBreadcrumb[], - options?: { deeperContextServerless: boolean } -) => { +export const useMetricsBreadcrumbs = (extraCrumbs: ChromeBreadcrumb[]) => { const { services: { serverless }, } = useKibanaContextForPlugin(); const appLinkProps = useLinkProps({ app: METRICS_APP }); - const breadcrumbs = useMemo( - () => [ - { - ...appLinkProps, - text: metricsTitle, - }, - ...extraCrumbs, - ], - [appLinkProps, extraCrumbs] - ); + const breadcrumbs = [ + { + ...appLinkProps, + text: metricsTitle, + }, + ...extraCrumbs, + ]; - useBreadcrumbs(breadcrumbs); - - useEffect(() => { - // For deeper context breadcrumbs in serveless, the `serverless` plugin provides its own breadcrumb service. - // https://docs.elastic.dev/kibana-dev-docs/serverless-project-navigation#breadcrumbs - if (serverless && options?.deeperContextServerless) { - // The initial path is already set in the breadcrumbs - const [, ...serverlessBreadcrumbs] = breadcrumbs; - serverless.setBreadcrumbs(serverlessBreadcrumbs); - } - }, [breadcrumbs, options?.deeperContextServerless, serverless]); + useBreadcrumbs(breadcrumbs, { serverless }); }; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/lib/get_filtered_metrics.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/lib/get_filtered_metrics.ts index 36433a2bca016..a6cfd30eaa26d 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/lib/get_filtered_metrics.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/lib/get_filtered_metrics.ts @@ -18,11 +18,13 @@ export const getFilteredMetrics = ( .filter((data) => data && data.source === 'metrics') .map((data) => data && data.name); return requiredMetrics.filter((metric) => { - const metricModelCreator = metrics.tsvb[metric]; + const metricModelCreator = metrics?.tsvb[metric] ?? null; // 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'); - return metricMetadata.some((m) => m && metricModel.requires.includes(m)); + const metricModel = metricModelCreator + ? metricModelCreator(TIMESTAMP_FIELD, 'test', '>=1m') + : { requires: [''] }; // when tsvb is not defined (host & container) + return metricMetadata.some((m) => m && metricModel?.requires?.includes(m)); }); }; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/metric_detail_page.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/metric_detail_page.tsx index 65ab15a5713be..0e6183268ddda 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/metric_detail_page.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/metric_detail_page.tsx @@ -54,18 +54,15 @@ export const MetricDetailPage = () => { }); const breadcrumbOptions = parentBreadcrumbResolver.getBreadcrumbOptions(nodeType); - useMetricsBreadcrumbs( - [ - { - ...breadcrumbOptions.link, - text: breadcrumbOptions.text, - }, - { - text: name, - }, - ], - { deeperContextServerless: true } - ); + useMetricsBreadcrumbs([ + { + ...breadcrumbOptions.link, + text: breadcrumbOptions.text, + }, + { + text: name, + }, + ]); const [sideNav, setSideNav] = useState([]); diff --git a/x-pack/plugins/observability_solution/infra/public/plugin.ts b/x-pack/plugins/observability_solution/infra/public/plugin.ts index 2324c5a633e2a..c8217794acf70 100644 --- a/x-pack/plugins/observability_solution/infra/public/plugin.ts +++ b/x-pack/plugins/observability_solution/infra/public/plugin.ts @@ -13,6 +13,7 @@ import { DEFAULT_APP_CATEGORIES, PluginInitializerContext, AppDeepLinkLocations, + AppStatus, } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { @@ -20,8 +21,16 @@ import { MetricsExplorerLocatorParams, ObservabilityTriggerId, } from '@kbn/observability-shared-plugin/common'; -import { BehaviorSubject, combineLatest, from } from 'rxjs'; -import { map } from 'rxjs'; +import { + BehaviorSubject, + combineLatest, + distinctUntilChanged, + from, + of, + switchMap, + map, + firstValueFrom, +} from 'rxjs'; import type { EmbeddableApiContext } from '@kbn/presentation-publishing'; import { apiCanAddNewPanel } from '@kbn/presentation-containers'; import { IncompatibleActionError, ADD_PANEL_TRIGGER } from '@kbn/ui-actions-plugin/public'; @@ -34,6 +43,7 @@ import { } from '@kbn/observability-shared-plugin/common'; import { OBSERVABILITY_ENABLE_LOGS_STREAM } from '@kbn/management-settings-ids'; import { NavigationEntry } from '@kbn/observability-shared-plugin/public'; +import { OBSERVABILITY_LOGS_EXPLORER_APP_ID } from '@kbn/deeplinks-observability/constants'; import type { InfraPublicConfig } from '../common/plugin_config_types'; import { createInventoryMetricRuleType } from './alerting/inventory'; import { createLogThresholdRuleType } from './alerting/log_threshold'; @@ -130,68 +140,64 @@ export class Plugin implements InfraClientPluginClass { messageFields: this.config.sources?.default?.fields?.message, }); - const startDep$AndHostViewFlag$ = combineLatest([from(core.getStartServices())]); + const startDep$AndAccessibleFlag$ = from(core.getStartServices()).pipe( + switchMap(([{ application }]) => + combineLatest([of(application), getLogsExplorerAccessible$(application)]) + ) + ); const logRoutes = getLogsAppRoutes({ isLogsStreamEnabled }); /** !! Need to be kept in sync with the deepLinks in x-pack/plugins/observability_solution/infra/public/plugin.ts */ pluginsSetup.observabilityShared.navigation.registerSections( - startDep$AndHostViewFlag$.pipe( - map( - ([ - [ - { - application: { capabilities }, - }, - ], - ]) => { - const { infrastructure, logs } = capabilities; - return [ - ...(logs.show - ? [ - { - label: logsTitle, - sortKey: 200, - entries: getLogsNavigationEntries({ - capabilities, - config: this.config, - routes: logRoutes, - }), - }, - ] - : []), - ...(infrastructure.show - ? [ - { - label: metricsTitle, - sortKey: 300, - entries: [ - { - label: inventoryTitle, - app: 'metrics', - path: '/inventory', - }, - ...(this.config.featureFlags.metricsExplorerEnabled - ? [ - { - label: metricsExplorerTitle, - app: 'metrics', - path: '/explorer', - }, - ] - : []), - { - label: hostsTitle, - app: 'metrics', - path: '/hosts', - }, - ], - }, - ] - : []), - ]; - } - ) + startDep$AndAccessibleFlag$.pipe( + map(([application, isLogsExplorerAccessible]) => { + const { infrastructure, logs } = application.capabilities; + return [ + ...(logs.show + ? [ + { + label: logsTitle, + sortKey: 200, + entries: getLogsNavigationEntries({ + isLogsExplorerAccessible, + config: this.config, + routes: logRoutes, + }), + }, + ] + : []), + ...(infrastructure.show + ? [ + { + label: metricsTitle, + sortKey: 300, + entries: [ + { + label: inventoryTitle, + app: 'metrics', + path: '/inventory', + }, + ...(this.config.featureFlags.metricsExplorerEnabled + ? [ + { + label: metricsExplorerTitle, + app: 'metrics', + path: '/explorer', + }, + ] + : []), + { + label: hostsTitle, + app: 'metrics', + path: '/hosts', + }, + ], + }, + ] + : []), + ]; + }) ) ); @@ -226,8 +232,12 @@ export class Plugin implements InfraClientPluginClass { // mount callback should not use setup dependencies, get start dependencies instead const [coreStart, plugins, pluginStart] = await core.getStartServices(); + const isLogsExplorerAccessible = await firstValueFrom( + getLogsExplorerAccessible$(coreStart.application) + ); + const { renderApp } = await import('./apps/logs_app'); - return renderApp(coreStart, plugins, pluginStart, params); + return renderApp(coreStart, plugins, pluginStart, isLogsExplorerAccessible, params); }, }); } @@ -316,16 +326,13 @@ export class Plugin implements InfraClientPluginClass { ); }, }); - - startDep$AndHostViewFlag$.subscribe( - ([_startServices]: [[CoreStart, InfraClientStartDeps, InfraClientStartExports]]) => { - this.appUpdater$.next(() => ({ - deepLinks: getInfraDeepLinks({ - metricsExplorerEnabled: this.config.featureFlags.metricsExplorerEnabled, - }), - })); - } - ); + startDep$AndAccessibleFlag$.subscribe(([_applicationStart, _isLogsExplorerAccessible]) => { + this.appUpdater$.next(() => ({ + deepLinks: getInfraDeepLinks({ + metricsExplorerEnabled: this.config.featureFlags.metricsExplorerEnabled, + }), + })); + }); // Setup telemetry events this.telemetry.setup({ analytics: core.analytics }); @@ -388,11 +395,11 @@ export class Plugin implements InfraClientPluginClass { } const getLogsNavigationEntries = ({ - capabilities, + isLogsExplorerAccessible, config, routes, }: { - capabilities: CoreStart['application']['capabilities']; + isLogsExplorerAccessible: boolean; config: InfraPublicConfig; routes: LogsAppRoutes; }) => { @@ -400,7 +407,7 @@ const getLogsNavigationEntries = ({ if (!config.featureFlags.logsUIEnabled) return entries; - if (capabilities.discover?.show && capabilities.fleet?.read) { + if (isLogsExplorerAccessible) { entries.push({ label: 'Explorer', app: 'observability-logs-explorer', @@ -420,6 +427,18 @@ const getLogsNavigationEntries = ({ return entries; }; +const getLogsExplorerAccessible$ = (application: CoreStart['application']) => { + const { applications$ } = application; + return applications$.pipe( + map( + (apps) => + (apps.get(OBSERVABILITY_LOGS_EXPLORER_APP_ID)?.status ?? AppStatus.inaccessible) === + AppStatus.accessible + ), + distinctUntilChanged() + ); +}; + const createNavEntryFromRoute = ({ path, title }: LogsRoute): NavigationEntry => ({ app: 'logs', label: title, diff --git a/x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_client.mock.ts b/x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_client.mock.ts index d5ea4958b95f2..2f68c4f5501c6 100644 --- a/x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_client.mock.ts +++ b/x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_client.mock.ts @@ -21,4 +21,8 @@ export const createTelemetryClientMock = (): jest.Mocked => ({ reportAddMetricsCalloutTryItClicked: jest.fn(), reportAddMetricsCalloutLearnMoreClicked: jest.fn(), reportAddMetricsCalloutDismissed: jest.fn(), + reportAnomalyDetectionSetup: jest.fn(), + reportAnomalyDetectionDateFieldChange: jest.fn(), + reportAnomalyDetectionFilterFieldChange: jest.fn(), + reportAnomalyDetectionPartitionFieldChange: jest.fn(), }); diff --git a/x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_client.ts b/x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_client.ts index d5dcf9d3f0c8d..0adf6a04dea7f 100644 --- a/x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_client.ts +++ b/x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_client.ts @@ -9,6 +9,10 @@ import type { AnalyticsServiceSetup } from '@kbn/core-analytics-server'; import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; import { AddMetricsCalloutEventParams, + AnomalyDetectionDateFieldChangeParams, + AnomalyDetectionFilterFieldChangeParams, + AnomalyDetectionPartitionFieldChangeParams, + AnomalyDetectionSetupParams, AssetDashboardLoadedParams, AssetDetailsFlyoutViewedParams, AssetDetailsPageViewedParams, @@ -114,4 +118,35 @@ export class TelemetryClient implements ITelemetryClient { public reportAddMetricsCalloutDismissed = (params: AddMetricsCalloutEventParams) => { this.analytics.reportEvent(InfraTelemetryEventTypes.ADD_METRICS_CALLOUT_DISMISSED, params); }; + + public reportAnomalyDetectionSetup = (params: AnomalyDetectionSetupParams) => { + this.analytics.reportEvent(InfraTelemetryEventTypes.ANOMALY_DETECTION_SETUP, params); + }; + + public reportAnomalyDetectionDateFieldChange = ( + params: AnomalyDetectionDateFieldChangeParams + ) => { + this.analytics.reportEvent( + InfraTelemetryEventTypes.ANOMALY_DETECTION_DATE_FIELD_CHANGE, + params + ); + }; + + public reportAnomalyDetectionPartitionFieldChange = ( + params: AnomalyDetectionPartitionFieldChangeParams + ) => { + this.analytics.reportEvent( + InfraTelemetryEventTypes.ANOMALY_DETECTION_PARTITION_FIELD_CHANGE, + params + ); + }; + + public reportAnomalyDetectionFilterFieldChange = ( + params: AnomalyDetectionFilterFieldChangeParams + ) => { + this.analytics.reportEvent( + InfraTelemetryEventTypes.ANOMALY_DETECTION_FILTER_FIELD_CHANGE, + params + ); + }; } diff --git a/x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_events.ts b/x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_events.ts index ec2f918354cbf..7f025c1051755 100644 --- a/x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_events.ts +++ b/x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_events.ts @@ -264,6 +264,98 @@ const addMetricsCalloutDismissed: InfraTelemetryEvent = { }, }; +const anomalyDetectionSetup: InfraTelemetryEvent = { + eventType: InfraTelemetryEventTypes.ANOMALY_DETECTION_SETUP, + schema: { + job_type: { + type: 'text', + _meta: { + description: 'Type of job for the anomaly detection', + }, + }, + configured_fields: { + properties: { + start_date: { + type: 'text', + _meta: { + description: 'Start date for the anomaly detection job', + }, + }, + partition_field: { + type: 'text', + _meta: { + description: 'Partition field for the anomaly detection job', + optional: true, + }, + }, + filter_field: { + type: 'text', + _meta: { + description: 'Filter field for the anomaly detection job', + optional: true, + }, + }, + }, + }, + }, +}; + +const anomalyDetectionDateFieldChange: InfraTelemetryEvent = { + eventType: InfraTelemetryEventTypes.ANOMALY_DETECTION_DATE_FIELD_CHANGE, + schema: { + job_type: { + type: 'text', + _meta: { + description: 'Type of job for the anomaly detection', + }, + }, + start_date: { + type: 'text', + _meta: { + description: 'Start date for the anomaly detection job', + }, + }, + }, +}; + +const anomalyDetectionPartitionFieldChange: InfraTelemetryEvent = { + eventType: InfraTelemetryEventTypes.ANOMALY_DETECTION_PARTITION_FIELD_CHANGE, + schema: { + job_type: { + type: 'text', + _meta: { + description: 'Type of job for the anomaly detection', + }, + }, + partition_field: { + type: 'text', + _meta: { + description: 'Partition field for the anomaly detection job', + optional: true, + }, + }, + }, +}; + +const anomalyDetectionFilterFieldChange: InfraTelemetryEvent = { + eventType: InfraTelemetryEventTypes.ANOMALY_DETECTION_FILTER_FIELD_CHANGE, + schema: { + job_type: { + type: 'text', + _meta: { + description: 'Type of job for the anomaly detection', + }, + }, + filter_field: { + type: 'text', + _meta: { + description: 'Filter field for the anomaly detection job', + optional: true, + }, + }, + }, +}; + export const infraTelemetryEvents = [ assetDetailsFlyoutViewed, assetDetailsPageViewed, @@ -277,4 +369,8 @@ export const infraTelemetryEvents = [ addMetricsCalloutTryItClicked, addMetricsCalloutLearnMoreClicked, addMetricsCalloutDismissed, + anomalyDetectionSetup, + anomalyDetectionDateFieldChange, + anomalyDetectionPartitionFieldChange, + anomalyDetectionFilterFieldChange, ]; diff --git a/x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts b/x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts index 78f4d0e64d792..afce0e37126a5 100644 --- a/x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts +++ b/x-pack/plugins/observability_solution/infra/public/services/telemetry/telemetry_service.test.ts @@ -52,6 +52,19 @@ describe('TelemetryService', () => { expect(telemetry).toHaveProperty('reportHostFlyoutFilterRemoved'); expect(telemetry).toHaveProperty('reportHostFlyoutFilterAdded'); expect(telemetry).toHaveProperty('reportHostsViewQuerySubmitted'); + expect(telemetry).toHaveProperty('reportHostsViewTotalHostCountRetrieved'); + expect(telemetry).toHaveProperty('reportAssetDetailsFlyoutViewed'); + expect(telemetry).toHaveProperty('reportAssetDetailsPageViewed'); + expect(telemetry).toHaveProperty('reportPerformanceMetricEvent'); + expect(telemetry).toHaveProperty('reportAssetDashboardLoaded'); + expect(telemetry).toHaveProperty('reportAddMetricsCalloutAddMetricsClicked'); + expect(telemetry).toHaveProperty('reportAddMetricsCalloutTryItClicked'); + expect(telemetry).toHaveProperty('reportAddMetricsCalloutLearnMoreClicked'); + expect(telemetry).toHaveProperty('reportAddMetricsCalloutDismissed'); + expect(telemetry).toHaveProperty('reportAnomalyDetectionSetup'); + expect(telemetry).toHaveProperty('reportAnomalyDetectionDateFieldChange'); + expect(telemetry).toHaveProperty('reportAnomalyDetectionPartitionFieldChange'); + expect(telemetry).toHaveProperty('reportAnomalyDetectionFilterFieldChange'); }); }); @@ -343,4 +356,99 @@ describe('TelemetryService', () => { ); }); }); + + describe('#reportAnomalyDetectionSetup', () => { + it('should report anomaly detection setup with properties', async () => { + const setupParams = getSetupParams(); + service.setup(setupParams); + const telemetry = service.start(); + const jobType = 'host'; + const configuredFields = { + start_date: new Date().toISOString(), + partition_field: 'partitionField', + filter_field: 'filterField', + }; + + telemetry.reportAnomalyDetectionSetup({ + job_type: jobType, + configured_fields: configuredFields, + }); + + expect(setupParams.analytics.reportEvent).toHaveBeenCalledTimes(1); + expect(setupParams.analytics.reportEvent).toHaveBeenCalledWith( + 'Infra Anomaly Detection Job Setup', + { + job_type: jobType, + configured_fields: configuredFields, + } + ); + }); + }); + + describe('#reportAnomalyDetectionDateFieldChange', () => { + it('should report anomaly detection date field change with properties', async () => { + const setupParams = getSetupParams(); + service.setup(setupParams); + const telemetry = service.start(); + const startDate = new Date().toISOString(); + + telemetry.reportAnomalyDetectionDateFieldChange({ + job_type: 'host', + start_date: startDate, + }); + + expect(setupParams.analytics.reportEvent).toHaveBeenCalledTimes(1); + expect(setupParams.analytics.reportEvent).toHaveBeenCalledWith( + 'Infra Anomaly Detection Job Date Field Change', + { + job_type: 'host', + start_date: startDate, + } + ); + }); + }); + + describe('#reportAnomalyDetectionPartitionFieldChange', () => { + it('should report anomaly detection partition field change with properties', async () => { + const setupParams = getSetupParams(); + service.setup(setupParams); + const telemetry = service.start(); + + telemetry.reportAnomalyDetectionPartitionFieldChange({ + job_type: 'host', + partition_field: 'partitionField', + }); + + expect(setupParams.analytics.reportEvent).toHaveBeenCalledTimes(1); + expect(setupParams.analytics.reportEvent).toHaveBeenCalledWith( + 'Infra Anomaly Detection Job Partition Field Change', + { + job_type: 'host', + partition_field: 'partitionField', + } + ); + }); + }); + + describe('#reportAnomalyDetectionFilterFieldChange', () => { + it('should report anomaly detection filter field change with properties', async () => { + const setupParams = getSetupParams(); + service.setup(setupParams); + const telemetry = service.start(); + + telemetry.reportAnomalyDetectionFilterFieldChange({ + job_type: 'host', + filter_field: 'filterField', + }); + + expect(setupParams.analytics.reportEvent).toHaveBeenCalledTimes(1); + expect(setupParams.analytics.reportEvent).toHaveBeenCalledWith( + 'Infra Anomaly Detection Job Filter Field Change', + { + job_type: 'host', + filter_field: 'filterField', + } + ); + }); + }); }); diff --git a/x-pack/plugins/observability_solution/infra/public/services/telemetry/types.ts b/x-pack/plugins/observability_solution/infra/public/services/telemetry/types.ts index 14816421fe695..982fddfd9492c 100644 --- a/x-pack/plugins/observability_solution/infra/public/services/telemetry/types.ts +++ b/x-pack/plugins/observability_solution/infra/public/services/telemetry/types.ts @@ -26,6 +26,10 @@ export enum InfraTelemetryEventTypes { ADD_METRICS_CALLOUT_TRY_IT_CLICKED = 'Add Metrics Callout Try It Clicked', ADD_METRICS_CALLOUT_LEARN_MORE_CLICKED = 'Add Metrics Callout Learn More Clicked', ADD_METRICS_CALLOUT_DISMISSED = 'Add Metrics Callout Dismissed', + ANOMALY_DETECTION_SETUP = 'Infra Anomaly Detection Job Setup', + ANOMALY_DETECTION_DATE_FIELD_CHANGE = 'Infra Anomaly Detection Job Date Field Change', + ANOMALY_DETECTION_PARTITION_FIELD_CHANGE = 'Infra Anomaly Detection Job Partition Field Change', + ANOMALY_DETECTION_FILTER_FIELD_CHANGE = 'Infra Anomaly Detection Job Filter Field Change', } export interface HostsViewQuerySubmittedParams { @@ -69,6 +73,26 @@ export interface AddMetricsCalloutEventParams { view: string; } +export interface AnomalyDetectionSetupParams { + job_type: string; + configured_fields: { start_date: string; partition_field?: string; filter_field?: string }; +} + +export interface AnomalyDetectionDateFieldChangeParams { + job_type: string; + start_date: string; +} + +export interface AnomalyDetectionPartitionFieldChangeParams { + job_type: string; + partition_field?: string; +} + +export interface AnomalyDetectionFilterFieldChangeParams { + job_type: string; + filter_field?: string; +} + export type InfraTelemetryEventParams = | HostsViewQuerySubmittedParams | HostEntryClickedParams @@ -76,7 +100,11 @@ export type InfraTelemetryEventParams = | HostsViewQueryHostsCountRetrievedParams | AssetDetailsFlyoutViewedParams | AssetDashboardLoadedParams - | AddMetricsCalloutEventParams; + | AddMetricsCalloutEventParams + | AnomalyDetectionSetupParams + | AnomalyDetectionDateFieldChangeParams + | AnomalyDetectionPartitionFieldChangeParams + | AnomalyDetectionFilterFieldChangeParams; export interface PerformanceMetricInnerEvents { key1?: string; @@ -102,6 +130,12 @@ export interface ITelemetryClient { reportAddMetricsCalloutTryItClicked(params: AddMetricsCalloutEventParams): void; reportAddMetricsCalloutLearnMoreClicked(params: AddMetricsCalloutEventParams): void; reportAddMetricsCalloutDismissed(params: AddMetricsCalloutEventParams): void; + reportAnomalyDetectionSetup(params: AnomalyDetectionSetupParams): void; + reportAnomalyDetectionDateFieldChange(params: AnomalyDetectionDateFieldChangeParams): void; + reportAnomalyDetectionPartitionFieldChange( + params: AnomalyDetectionPartitionFieldChangeParams + ): void; + reportAnomalyDetectionFilterFieldChange(params: AnomalyDetectionFilterFieldChangeParams): void; } export type InfraTelemetryEvent = @@ -152,4 +186,20 @@ export type InfraTelemetryEvent = | { eventType: InfraTelemetryEventTypes.ADD_METRICS_CALLOUT_DISMISSED; schema: RootSchema; + } + | { + eventType: InfraTelemetryEventTypes.ANOMALY_DETECTION_SETUP; + schema: RootSchema; + } + | { + eventType: InfraTelemetryEventTypes.ANOMALY_DETECTION_DATE_FIELD_CHANGE; + schema: RootSchema; + } + | { + eventType: InfraTelemetryEventTypes.ANOMALY_DETECTION_PARTITION_FIELD_CHANGE; + schema: RootSchema; + } + | { + eventType: InfraTelemetryEventTypes.ANOMALY_DETECTION_FILTER_FIELD_CHANGE; + schema: RootSchema; }; diff --git a/x-pack/plugins/observability_solution/infra/public/translations.ts b/x-pack/plugins/observability_solution/infra/public/translations.ts index ecb72b3df4b01..3377ae1dd1fa1 100644 --- a/x-pack/plugins/observability_solution/infra/public/translations.ts +++ b/x-pack/plugins/observability_solution/infra/public/translations.ts @@ -39,7 +39,7 @@ export const metricsTitle = i18n.translate('xpack.infra.header.infrastructureTit }); export const inventoryTitle = i18n.translate('xpack.infra.metrics.infrastructureInventoryTitle', { - defaultMessage: 'Infrastructure Inventory', + defaultMessage: 'Infrastructure inventory', }); export const metricsExplorerTitle = i18n.translate('xpack.infra.metrics.metricsExplorerTitle', { diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts index c54b29d52714f..a29308774440c 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts @@ -6,14 +6,7 @@ */ import rison from '@kbn/rison'; -import { - AlertInstanceContext as AlertContext, - AlertInstanceState as AlertState, -} from '@kbn/alerting-plugin/server'; import { RuleExecutorServicesMock, alertsMock } from '@kbn/alerting-plugin/server/mocks'; -import { LifecycleAlertServices } from '@kbn/rule-registry-plugin/server'; -import { ruleRegistryMocks } from '@kbn/rule-registry-plugin/server/mocks'; -import { createLifecycleRuleExecutorMock } from '@kbn/rule-registry-plugin/server/utils/create_lifecycle_rule_executor_mock'; import { COMPARATORS } from '@kbn/alerting-comparators'; import { Aggregators, InventoryMetricConditions } from '../../../../common/alerting/metrics'; import type { LogMeta, Logger } from '@kbn/logging'; @@ -150,9 +143,7 @@ const mockLibs = { infraPluginMock.createStartContract(), ], configuration: createMockStaticConfiguration({}), - metricsRules: { - createLifecycleRuleExecutor: createLifecycleRuleExecutorMock, - }, + metricsRules: {}, basePath: { publicBaseUrl: 'http://localhost:5601', prepend: (path: string) => path, @@ -165,14 +156,10 @@ const mockLibs = { logger, } as unknown as InfraBackendLibs; const alerts = new Map(); -let services: RuleExecutorServicesMock & LifecycleAlertServices; +let services: RuleExecutorServicesMock; const setup = () => { - const alertsServices = alertsMock.createRuleExecutorServices(); - services = { - ...alertsServices, - ...ruleRegistryMocks.createLifecycleAlertServices(alertsServices), - }; + services = alertsMock.createRuleExecutorServices(); services.alertsClient.report.mockImplementation((params: any) => { alerts.set(params.id, { actionGroup: params.actionGroup, context: [], payload: [] }); diff --git a/x-pack/plugins/observability_solution/infra/server/routes/entities/index.ts b/x-pack/plugins/observability_solution/infra/server/routes/entities/index.ts index cb169f83f171d..1a8707678e8f7 100644 --- a/x-pack/plugins/observability_solution/infra/server/routes/entities/index.ts +++ b/x-pack/plugins/observability_solution/infra/server/routes/entities/index.ts @@ -9,6 +9,7 @@ import { schema } from '@kbn/config-schema'; import { METRICS_APP_ID } from '@kbn/deeplinks-observability/constants'; import { entityCentricExperience } from '@kbn/observability-plugin/common'; import { createObservabilityEsClient } from '@kbn/observability-utils/es/client/create_observability_es_client'; +import { ENTITY_TYPES } from '@kbn/observability-shared-plugin/common'; import { getInfraMetricsClient } from '../../lib/helpers/get_infra_metrics_client'; import { InfraBackendLibs } from '../../lib/infra_types'; import { getDataStreamTypes } from './get_data_stream_types'; @@ -22,7 +23,10 @@ export const initEntitiesConfigurationRoutes = (libs: InfraBackendLibs) => { path: '/api/infra/entities/{entityType}/{entityId}/summary', validate: { params: schema.object({ - entityType: schema.oneOf([schema.literal('host'), schema.literal('container')]), + entityType: schema.oneOf([ + schema.literal(ENTITY_TYPES.HOST), + schema.literal(ENTITY_TYPES.CONTAINER), + ]), entityId: schema.string(), }), }, diff --git a/x-pack/plugins/observability_solution/infra/server/services/rules/rules_service.ts b/x-pack/plugins/observability_solution/infra/server/services/rules/rules_service.ts index 85d3d8548fbe6..99e7c57d857b5 100644 --- a/x-pack/plugins/observability_solution/infra/server/services/rules/rules_service.ts +++ b/x-pack/plugins/observability_solution/infra/server/services/rules/rules_service.ts @@ -6,7 +6,6 @@ */ import { CoreSetup, Logger } from '@kbn/core/server'; -import { createLifecycleExecutor } from '@kbn/rule-registry-plugin/server'; import { InfraFeatureId } from '../../../common/constants'; import { createRuleDataClient } from './rule_data_client'; import { @@ -36,12 +35,7 @@ export class RulesService { ruleDataService: setupDeps.ruleRegistry.ruleDataService, }); - const createLifecycleRuleExecutor = createLifecycleExecutor(this.logger, ruleDataClient); - - return { - createLifecycleRuleExecutor, - ruleDataClient, - }; + return { ruleDataClient }; } public start(_startDeps: RulesServiceStartDeps): RulesServiceStart { diff --git a/x-pack/plugins/observability_solution/infra/server/services/rules/types.ts b/x-pack/plugins/observability_solution/infra/server/services/rules/types.ts index fa14089de2ba5..68ae0bd95b410 100644 --- a/x-pack/plugins/observability_solution/infra/server/services/rules/types.ts +++ b/x-pack/plugins/observability_solution/infra/server/services/rules/types.ts @@ -6,13 +6,7 @@ */ import { PluginSetupContract as AlertingPluginSetup } from '@kbn/alerting-plugin/server'; -import { - createLifecycleExecutor, - IRuleDataClient, - RuleRegistryPluginSetupContract, -} from '@kbn/rule-registry-plugin/server'; - -type LifecycleRuleExecutorCreator = ReturnType; +import { IRuleDataClient, RuleRegistryPluginSetupContract } from '@kbn/rule-registry-plugin/server'; export interface RulesServiceSetupDeps { alerting: AlertingPluginSetup; ruleRegistry: RuleRegistryPluginSetupContract; @@ -22,7 +16,6 @@ export interface RulesServiceSetupDeps { export interface RulesServiceStartDeps {} export interface RulesServiceSetup { - createLifecycleRuleExecutor: LifecycleRuleExecutorCreator; ruleDataClient: IRuleDataClient; } diff --git a/x-pack/plugins/observability_solution/inventory/common/entities.ts b/x-pack/plugins/observability_solution/inventory/common/entities.ts index d8a056074e339..f686490b90bfc 100644 --- a/x-pack/plugins/observability_solution/inventory/common/entities.ts +++ b/x-pack/plugins/observability_solution/inventory/common/entities.ts @@ -4,7 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { ENTITY_LATEST, entitiesAliasPattern } from '@kbn/entities-schema'; +import { z } from '@kbn/zod'; +import { ENTITY_LATEST, entitiesAliasPattern, entityLatestSchema } from '@kbn/entities-schema'; import { ENTITY_DEFINITION_ID, ENTITY_DISPLAY_NAME, @@ -13,6 +14,7 @@ import { ENTITY_LAST_SEEN, ENTITY_TYPE, } from '@kbn/observability-shared-plugin/common'; +import { decode, encode } from '@kbn/rison'; import { isRight } from 'fp-ts/lib/Either'; import * as t from 'io-ts'; @@ -25,6 +27,49 @@ export const entityColumnIdsRt = t.union([ export type EntityColumnIds = t.TypeOf; +export const entityViewRt = t.union([t.literal('unified'), t.literal('grouped')]); + +const paginationRt = t.record(t.string, t.number); +export const entityPaginationRt = new t.Type | undefined, string, unknown>( + 'entityPaginationRt', + paginationRt.is, + (input, context) => { + switch (typeof input) { + case 'string': { + try { + const decoded = decode(input); + const validation = paginationRt.decode(decoded); + if (isRight(validation)) { + return t.success(validation.right); + } + + return t.failure(input, context); + } catch (e) { + return t.failure(input, context); + } + } + + case 'undefined': + return t.success(input); + + default: { + const validation = paginationRt.decode(input); + + if (isRight(validation)) { + return t.success(validation.right); + } + + return t.failure(input, context); + } + } + }, + (o) => encode(o) +); + +export type EntityView = t.TypeOf; + +export type EntityPagination = t.TypeOf; + export const defaultEntitySortField: EntityColumnIds = 'alertsCount'; export const MAX_NUMBER_OF_ENTITIES = 500; @@ -67,3 +112,13 @@ export interface Entity { alertsCount?: number; [key: string]: any; } + +export type EntityGroup = { + count: number; +} & { + [key: string]: any; +}; + +export type InventoryEntityLatest = z.infer & { + alertsCount?: number; +}; diff --git a/x-pack/plugins/observability_solution/inventory/common/utils/parse_identity_field_values_to_kql.test.ts b/x-pack/plugins/observability_solution/inventory/common/utils/parse_identity_field_values_to_kql.test.ts deleted file mode 100644 index 8703e995b4446..0000000000000 --- a/x-pack/plugins/observability_solution/inventory/common/utils/parse_identity_field_values_to_kql.test.ts +++ /dev/null @@ -1,91 +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 { - ENTITY_DEFINITION_ID, - ENTITY_DISPLAY_NAME, - ENTITY_ID, - ENTITY_IDENTITY_FIELDS, - ENTITY_LAST_SEEN, -} from '@kbn/observability-shared-plugin/common'; -import type { Entity } from '../entities'; -import { parseIdentityFieldValuesToKql } from './parse_identity_field_values_to_kql'; - -const commonEntityFields = { - [ENTITY_LAST_SEEN]: '2023-10-09T00:00:00Z', - [ENTITY_ID]: '1', - [ENTITY_DISPLAY_NAME]: 'entity_name', - [ENTITY_DEFINITION_ID]: 'entity_definition_id', - alertCount: 3, -}; - -describe('parseIdentityFieldValuesToKql', () => { - it('should return the value when identityFields is a single string', () => { - const entity: Entity = { - 'agent.name': 'node', - [ENTITY_IDENTITY_FIELDS]: 'service.name', - 'service.name': 'my-service', - 'entity.type': 'service', - ...commonEntityFields, - }; - - const result = parseIdentityFieldValuesToKql({ entity }); - expect(result).toEqual('service.name: "my-service"'); - }); - - it('should return values when identityFields is an array of strings', () => { - const entity: Entity = { - 'agent.name': 'node', - [ENTITY_IDENTITY_FIELDS]: ['service.name', 'service.environment'], - 'service.name': 'my-service', - 'entity.type': 'service', - 'service.environment': 'staging', - ...commonEntityFields, - }; - - const result = parseIdentityFieldValuesToKql({ entity }); - expect(result).toEqual('service.name: "my-service" AND service.environment: "staging"'); - }); - - it('should return an empty string if identityFields is empty string', () => { - const entity: Entity = { - 'agent.name': 'node', - [ENTITY_IDENTITY_FIELDS]: '', - 'service.name': 'my-service', - 'entity.type': 'service', - ...commonEntityFields, - }; - - const result = parseIdentityFieldValuesToKql({ entity }); - expect(result).toEqual(''); - }); - it('should return an empty array if identityFields is empty array', () => { - const entity: Entity = { - 'agent.name': 'node', - [ENTITY_IDENTITY_FIELDS]: [], - 'service.name': 'my-service', - 'entity.type': 'service', - ...commonEntityFields, - }; - - const result = parseIdentityFieldValuesToKql({ entity }); - expect(result).toEqual(''); - }); - - it('should ignore fields that are not present in the entity', () => { - const entity: Entity = { - [ENTITY_IDENTITY_FIELDS]: ['host.name', 'foo.bar'], - 'host.name': 'my-host', - 'entity.type': 'host', - 'cloud.provider': null, - ...commonEntityFields, - }; - - const result = parseIdentityFieldValuesToKql({ entity }); - expect(result).toEqual('host.name: "my-host"'); - }); -}); diff --git a/x-pack/plugins/observability_solution/inventory/common/utils/parse_identity_field_values_to_kql.ts b/x-pack/plugins/observability_solution/inventory/common/utils/parse_identity_field_values_to_kql.ts deleted file mode 100644 index 2e3f3dadd4109..0000000000000 --- a/x-pack/plugins/observability_solution/inventory/common/utils/parse_identity_field_values_to_kql.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ENTITY_IDENTITY_FIELDS } from '@kbn/observability-shared-plugin/common'; -import { Entity } from '../entities'; - -type Operator = 'AND'; -export function parseIdentityFieldValuesToKql({ - entity, - operator = 'AND', -}: { - entity: Entity; - operator?: Operator; -}) { - const mapping: string[] = []; - - const identityFields = entity[ENTITY_IDENTITY_FIELDS]; - - if (identityFields) { - const fields = [identityFields].flat(); - - fields.forEach((field) => { - if (field in entity) { - mapping.push(`${[field]}: "${entity[field as keyof Entity]}"`); - } - }); - } - - return mapping.join(` ${operator} `); -} diff --git a/x-pack/plugins/observability_solution/inventory/common/utils/unflatten_entity.ts b/x-pack/plugins/observability_solution/inventory/common/utils/unflatten_entity.ts new file mode 100644 index 0000000000000..758d185a5753b --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/common/utils/unflatten_entity.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 { unflattenObject } from '@kbn/observability-utils/object/unflatten_object'; +import type { Entity, InventoryEntityLatest } from '../entities'; + +export function unflattenEntity(entity: Entity) { + return unflattenObject(entity) as InventoryEntityLatest; +} diff --git a/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/home.cy.ts b/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/home.cy.ts index c18f8866475ab..501b6b8078da5 100644 --- a/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/home.cy.ts +++ b/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/home.cy.ts @@ -59,12 +59,38 @@ describe('Home page', () => { logsSynthtrace.clean(); }); - it('Shows inventory page with entities', () => { + it('Shows inventory page with groups & entities', () => { cy.intercept('GET', '/internal/entities/managed/enablement', { fixture: 'eem_enabled.json', }).as('getEEMStatus'); + cy.intercept('GET', '/internal/inventory/entities?**').as('getEntities'); cy.visitKibana('/app/inventory'); cy.wait('@getEEMStatus'); + cy.contains('host'); + cy.getByTestSubj('inventoryGroupTitle_entity.type_host').click(); + cy.wait('@getEntities'); + cy.contains('service'); + cy.getByTestSubj('inventoryGroupTitle_entity.type_service').click(); + cy.wait('@getEntities'); + cy.contains('container'); + cy.getByTestSubj('inventoryGroupTitle_entity.type_container').click(); + cy.wait('@getEntities'); + cy.contains('server1'); + cy.contains('synth-node-trace-logs'); + cy.contains('foo'); + }); + + it('Shows inventory page with unified view of entities', () => { + cy.intercept('GET', '/internal/entities/managed/enablement', { + fixture: 'eem_enabled.json', + }).as('getEEMStatus'); + cy.intercept('GET', '/internal/inventory/entities?**').as('getEntities'); + cy.visitKibana('/app/inventory'); + cy.wait('@getEEMStatus'); + cy.contains('Group entities by: Type'); + cy.getByTestSubj('groupSelectorDropdown').click(); + cy.getByTestSubj('panelUnified').click(); + cy.wait('@getEntities'); cy.contains('server1'); cy.contains('host'); cy.contains('synth-node-trace-logs'); @@ -79,6 +105,7 @@ describe('Home page', () => { }).as('getEEMStatus'); cy.visitKibana('/app/inventory'); cy.wait('@getEEMStatus'); + cy.contains('service').click(); cy.contains('synth-node-trace-logs').click(); cy.url().should('include', '/app/apm/services/synth-node-trace-logs/overview'); }); @@ -89,16 +116,47 @@ describe('Home page', () => { }).as('getEEMStatus'); cy.visitKibana('/app/inventory'); cy.wait('@getEEMStatus'); + cy.contains('host').click(); cy.contains('server1').click(); cy.url().should('include', '/app/metrics/detail/host/server1'); }); + it('Navigates to discover with default filter', () => { + cy.intercept('GET', '/internal/entities/managed/enablement', { + fixture: 'eem_enabled.json', + }).as('getEEMStatus'); + cy.visitKibana('/app/inventory'); + cy.wait('@getEEMStatus'); + cy.contains('Open in discover').click(); + cy.url().should( + 'include', + "query:(language:kuery,query:'entity.definition_id%20:%20builtin*" + ); + }); + + it('Navigates to discover with kuery filter', () => { + cy.intercept('GET', '/internal/entities/managed/enablement', { + fixture: 'eem_enabled.json', + }).as('getEEMStatus'); + cy.visitKibana('/app/inventory'); + cy.wait('@getEEMStatus'); + cy.getByTestSubj('queryInput').type('service.name : foo'); + + cy.contains('Update').click(); + cy.contains('Open in discover').click(); + cy.url().should( + 'include', + "query:'service.name%20:%20foo%20AND%20entity.definition_id%20:%20builtin*'" + ); + }); + it('Navigates to infra when clicking on a container type entity', () => { cy.intercept('GET', '/internal/entities/managed/enablement', { fixture: 'eem_enabled.json', }).as('getEEMStatus'); cy.visitKibana('/app/inventory'); cy.wait('@getEEMStatus'); + cy.contains('container').click(); cy.contains('foo').click(); cy.url().should('include', '/app/metrics/detail/container/foo'); }); @@ -107,51 +165,69 @@ describe('Home page', () => { cy.intercept('GET', '/internal/entities/managed/enablement', { fixture: 'eem_enabled.json', }).as('getEEMStatus'); - cy.intercept('GET', '/internal/inventory/entities*').as('getEntitites'); + cy.intercept('GET', '/internal/inventory/entities?**').as('getEntities'); + cy.intercept('GET', '/internal/inventory/entities/group_by/**').as('getGroups'); cy.visitKibana('/app/inventory'); cy.wait('@getEEMStatus'); cy.getByTestSubj('entityTypesFilterComboBox') .click() .getByTestSubj('entityTypesFilterserviceOption') .click(); - cy.wait('@getEntitites'); + cy.wait('@getGroups'); + cy.contains('service'); + cy.getByTestSubj('inventoryGroupTitle_entity.type_service').click(); + cy.wait('@getEntities'); cy.get('server1').should('not.exist'); cy.contains('synth-node-trace-logs'); - cy.get('foo').should('not.exist'); + cy.contains('foo').should('not.exist'); + cy.getByTestSubj('inventoryGroup_entity.type_host').should('not.exist'); + cy.getByTestSubj('inventoryGroup_entity.type_container').should('not.exist'); }); it('Filters entities by host type', () => { cy.intercept('GET', '/internal/entities/managed/enablement', { fixture: 'eem_enabled.json', }).as('getEEMStatus'); - cy.intercept('GET', '/internal/inventory/entities*').as('getEntitites'); + cy.intercept('GET', '/internal/inventory/entities?**').as('getEntities'); + cy.intercept('GET', '/internal/inventory/entities/group_by/**').as('getGroups'); cy.visitKibana('/app/inventory'); cy.wait('@getEEMStatus'); cy.getByTestSubj('entityTypesFilterComboBox') .click() .getByTestSubj('entityTypesFilterhostOption') .click(); - cy.wait('@getEntitites'); + cy.wait('@getGroups'); + cy.contains('host'); + cy.getByTestSubj('inventoryGroupTitle_entity.type_host').click(); + cy.wait('@getEntities'); cy.contains('server1'); - cy.get('synth-node-trace-logs').should('not.exist'); - cy.get('foo').should('not.exist'); + cy.contains('synth-node-trace-logs').should('not.exist'); + cy.contains('foo').should('not.exist'); + cy.getByTestSubj('inventoryGroup_entity.type_service').should('not.exist'); + cy.getByTestSubj('inventoryGroup_entity.type_container').should('not.exist'); }); it('Filters entities by container type', () => { cy.intercept('GET', '/internal/entities/managed/enablement', { fixture: 'eem_enabled.json', }).as('getEEMStatus'); - cy.intercept('GET', '/internal/inventory/entities*').as('getEntitites'); + cy.intercept('GET', '/internal/inventory/entities?**').as('getEntities'); + cy.intercept('GET', '/internal/inventory/entities/group_by/**').as('getGroups'); cy.visitKibana('/app/inventory'); cy.wait('@getEEMStatus'); cy.getByTestSubj('entityTypesFilterComboBox') .click() .getByTestSubj('entityTypesFiltercontainerOption') .click(); - cy.wait('@getEntitites'); - cy.get('server1').should('not.exist'); - cy.get('synth-node-trace-logs').should('not.exist'); + cy.wait('@getGroups'); + cy.contains('container'); + cy.getByTestSubj('inventoryGroupTitle_entity.type_container').click(); + cy.wait('@getEntities'); + cy.contains('server1').should('not.exist'); + cy.contains('synth-node-trace-logs').should('not.exist'); cy.contains('foo'); + cy.getByTestSubj('inventoryGroup_entity.type_host').should('not.exist'); + cy.getByTestSubj('inventoryGroup_entity.type_service').should('not.exist'); }); }); }); diff --git a/x-pack/plugins/observability_solution/inventory/public/components/alerts_badge/alerts_badge.test.tsx b/x-pack/plugins/observability_solution/inventory/public/components/alerts_badge/alerts_badge.test.tsx index 60124e7813bc4..b5244cb29f7fc 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/alerts_badge/alerts_badge.test.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/alerts_badge/alerts_badge.test.tsx @@ -5,22 +5,35 @@ * 2.0. */ import React from 'react'; -import { type KibanaReactContextValue } from '@kbn/kibana-react-plugin/public'; import { render, screen } from '@testing-library/react'; import { AlertsBadge } from './alerts_badge'; -import * as useKibana from '../../hooks/use_kibana'; +import { useKibana } from '../../hooks/use_kibana'; import type { Entity } from '../../../common/entities'; +jest.mock('../../hooks/use_kibana'); +const useKibanaMock = useKibana as jest.Mock; + describe('AlertsBadge', () => { - jest.spyOn(useKibana, 'useKibana').mockReturnValue({ - services: { - http: { - basePath: { - prepend: (path: string) => path, + const mockAsKqlFilter = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + + useKibanaMock.mockReturnValue({ + services: { + http: { + basePath: { + prepend: (path: string) => path, + }, + }, + entityManager: { + entityClient: { + asKqlFilter: mockAsKqlFilter, + }, }, }, - }, - } as unknown as KibanaReactContextValue); + }); + }); afterAll(() => { jest.clearAllMocks(); @@ -38,9 +51,11 @@ describe('AlertsBadge', () => { 'cloud.provider': null, alertsCount: 1, }; + mockAsKqlFilter.mockReturnValue('host.name: foo'); + render(); expect(screen.queryByTestId('inventoryAlertsBadgeLink')?.getAttribute('href')).toEqual( - '/app/observability/alerts?_a=(kuery:\'host.name: "foo"\',status:active)' + "/app/observability/alerts?_a=(kuery:'host.name: foo',status:active)" ); expect(screen.queryByTestId('inventoryAlertsBadgeLink')?.textContent).toEqual('1'); }); @@ -57,9 +72,11 @@ describe('AlertsBadge', () => { 'cloud.provider': null, alertsCount: 5, }; + mockAsKqlFilter.mockReturnValue('service.name: bar'); + render(); expect(screen.queryByTestId('inventoryAlertsBadgeLink')?.getAttribute('href')).toEqual( - '/app/observability/alerts?_a=(kuery:\'service.name: "bar"\',status:active)' + "/app/observability/alerts?_a=(kuery:'service.name: bar',status:active)" ); expect(screen.queryByTestId('inventoryAlertsBadgeLink')?.textContent).toEqual('5'); }); @@ -77,9 +94,12 @@ describe('AlertsBadge', () => { 'cloud.provider': null, alertsCount: 2, }; + + mockAsKqlFilter.mockReturnValue('service.name: bar AND service.environment: prod'); + render(); expect(screen.queryByTestId('inventoryAlertsBadgeLink')?.getAttribute('href')).toEqual( - '/app/observability/alerts?_a=(kuery:\'service.name: "bar" AND service.environment: "prod"\',status:active)' + "/app/observability/alerts?_a=(kuery:'service.name: bar AND service.environment: prod',status:active)" ); expect(screen.queryByTestId('inventoryAlertsBadgeLink')?.textContent).toEqual('2'); }); diff --git a/x-pack/plugins/observability_solution/inventory/public/components/alerts_badge/alerts_badge.tsx b/x-pack/plugins/observability_solution/inventory/public/components/alerts_badge/alerts_badge.tsx index ba1b992ff62c1..a5845a7b42dcf 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/alerts_badge/alerts_badge.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/alerts_badge/alerts_badge.tsx @@ -8,20 +8,21 @@ import React from 'react'; import rison from '@kbn/rison'; import { EuiBadge, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { Entity } from '../../../common/entities'; +import type { Entity } from '../../../common/entities'; +import { unflattenEntity } from '../../../common/utils/unflatten_entity'; import { useKibana } from '../../hooks/use_kibana'; -import { parseIdentityFieldValuesToKql } from '../../../common/utils/parse_identity_field_values_to_kql'; export function AlertsBadge({ entity }: { entity: Entity }) { const { services: { http: { basePath }, + entityManager, }, } = useKibana(); const activeAlertsHref = basePath.prepend( `/app/observability/alerts?_a=${rison.encode({ - kuery: parseIdentityFieldValuesToKql({ entity }), + kuery: entityManager.entityClient.asKqlFilter(unflattenEntity(entity)), status: 'active', })}` ); diff --git a/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entity_name/entity_name.test.tsx b/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entity_name/entity_name.test.tsx index 2e4f0c319edfc..d5d08ed415a40 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entity_name/entity_name.test.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entity_name/entity_name.test.tsx @@ -5,148 +5,65 @@ * 2.0. */ -import { type KibanaReactContextValue } from '@kbn/kibana-react-plugin/public'; -import * as useKibana from '../../../hooks/use_kibana'; -import { EntityName } from '.'; -import type { Entity } from '../../../../common/entities'; -import { render, screen } from '@testing-library/react'; import React from 'react'; -import { ASSET_DETAILS_LOCATOR_ID } from '@kbn/observability-shared-plugin/common/locators/infra/asset_details_locator'; +import { render, screen } from '@testing-library/react'; +import { EntityName } from '.'; +import { useDetailViewRedirect } from '../../../hooks/use_detail_view_redirect'; +import { Entity } from '../../../../common/entities'; +import { + ENTITY_DEFINITION_ID, + ENTITY_DISPLAY_NAME, + ENTITY_ID, + ENTITY_IDENTITY_FIELDS, + ENTITY_LAST_SEEN, + ENTITY_TYPE, +} from '@kbn/observability-shared-plugin/common'; + +jest.mock('../../../hooks/use_detail_view_redirect'); + +const useDetailViewRedirectMock = useDetailViewRedirect as jest.Mock; describe('EntityName', () => { - jest.spyOn(useKibana, 'useKibana').mockReturnValue({ - services: { - share: { - url: { - locators: { - get: (locatorId: string) => { - return { - getRedirectUrl: (params: { [key: string]: any }) => { - if (locatorId === ASSET_DETAILS_LOCATOR_ID) { - return `assets_url/${params.assetType}/${params.assetId}`; - } - return `services_url/${params.serviceName}?environment=${params.environment}`; - }, - }; - }, - }, - }, - }, - }, - } as unknown as KibanaReactContextValue); + const mockEntity: Entity = { + [ENTITY_LAST_SEEN]: '2023-10-09T00:00:00Z', + [ENTITY_ID]: '1', + [ENTITY_DISPLAY_NAME]: 'entity_name', + [ENTITY_DEFINITION_ID]: 'entity_definition_id', + [ENTITY_IDENTITY_FIELDS]: ['service.name', 'service.environment'], + [ENTITY_TYPE]: 'service', + }; - afterAll(() => { + beforeEach(() => { jest.clearAllMocks(); }); - it('returns host link', () => { - const entity: Entity = { - 'entity.last_seen_timestamp': 'foo', - 'entity.id': '1', - 'entity.type': 'host', - 'entity.display_name': 'foo', - 'entity.identity_fields': 'host.name', - 'host.name': 'foo', - 'entity.definition_id': 'host', - 'cloud.provider': null, - }; - render(); - expect(screen.queryByTestId('entityNameLink')?.getAttribute('href')).toEqual( - 'assets_url/host/foo' - ); - expect(screen.queryByTestId('entityNameDisplayName')?.textContent).toEqual('foo'); - }); + it('should render the entity name correctly', () => { + useDetailViewRedirectMock.mockReturnValue({ + getEntityRedirectUrl: jest.fn().mockReturnValue(null), + }); - it('returns container link', () => { - const entity: Entity = { - 'entity.last_seen_timestamp': 'foo', - 'entity.id': '1', - 'entity.type': 'container', - 'entity.display_name': 'foo', - 'entity.identity_fields': 'container.id', - 'container.id': 'foo', - 'entity.definition_id': 'container', - 'cloud.provider': null, - }; - render(); - expect(screen.queryByTestId('entityNameLink')?.getAttribute('href')).toEqual( - 'assets_url/container/foo' - ); - expect(screen.queryByTestId('entityNameDisplayName')?.textContent).toEqual('foo'); - }); + render(); - it('returns service link without environment', () => { - const entity: Entity = { - 'entity.last_seen_timestamp': 'foo', - 'entity.id': '1', - 'entity.type': 'service', - 'entity.display_name': 'foo', - 'entity.identity_fields': 'service.name', - 'service.name': 'foo', - 'entity.definition_id': 'service', - 'agent.name': 'bar', - }; - render(); - expect(screen.queryByTestId('entityNameLink')?.getAttribute('href')).toEqual( - 'services_url/foo?environment=undefined' - ); - expect(screen.queryByTestId('entityNameDisplayName')?.textContent).toEqual('foo'); + expect(screen.getByText('entity_name')).toBeInTheDocument(); }); - it('returns service link with environment', () => { - const entity: Entity = { - 'entity.last_seen_timestamp': 'foo', - 'entity.id': '1', - 'entity.type': 'service', - 'entity.display_name': 'foo', - 'entity.identity_fields': 'service.name', - 'service.name': 'foo', - 'entity.definition_id': 'service', - 'agent.name': 'bar', - 'service.environment': 'baz', - }; - render(); - expect(screen.queryByTestId('entityNameLink')?.getAttribute('href')).toEqual( - 'services_url/foo?environment=baz' - ); - expect(screen.queryByTestId('entityNameDisplayName')?.textContent).toEqual('foo'); - }); + it('should a link when getEntityRedirectUrl returns a URL', () => { + useDetailViewRedirectMock.mockReturnValue({ + getEntityRedirectUrl: jest.fn().mockReturnValue('http://foo.bar'), + }); - it('returns service link with first environment when it is an array', () => { - const entity: Entity = { - 'entity.last_seen_timestamp': 'foo', - 'entity.id': '1', - 'entity.type': 'service', - 'entity.display_name': 'foo', - 'entity.identity_fields': 'service.name', - 'service.name': 'foo', - 'entity.definition_id': 'service', - 'agent.name': 'bar', - 'service.environment': ['baz', 'bar', 'foo'], - }; - render(); - expect(screen.queryByTestId('entityNameLink')?.getAttribute('href')).toEqual( - 'services_url/foo?environment=baz' - ); - expect(screen.queryByTestId('entityNameDisplayName')?.textContent).toEqual('foo'); + render(); + + expect(screen.getByRole('link')).toHaveAttribute('href', 'http://foo.bar'); }); - it('returns service link identity fields is an array', () => { - const entity: Entity = { - 'entity.last_seen_timestamp': 'foo', - 'entity.id': '1', - 'entity.type': 'service', - 'entity.display_name': 'foo', - 'entity.identity_fields': ['service.name', 'service.environment'], - 'service.name': 'foo', - 'entity.definition_id': 'service', - 'agent.name': 'bar', - 'service.environment': 'baz', - }; - render(); - expect(screen.queryByTestId('entityNameLink')?.getAttribute('href')).toEqual( - 'services_url/foo?environment=baz' - ); - expect(screen.queryByTestId('entityNameDisplayName')?.textContent).toEqual('foo'); + it('should not render a link when getEntityRedirectUrl returns null', () => { + useDetailViewRedirectMock.mockReturnValue({ + getEntityRedirectUrl: jest.fn().mockReturnValue(null), + }); + + render(); + + expect(screen.queryByRole('link')).not.toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entity_name/index.tsx b/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entity_name/index.tsx index 982a616da8fda..e8db7013f8cb3 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entity_name/index.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entity_name/index.tsx @@ -6,19 +6,12 @@ */ import { EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui'; -import { - ASSET_DETAILS_LOCATOR_ID, - AssetDetailsLocatorParams, - ENTITY_DISPLAY_NAME, - ENTITY_IDENTITY_FIELDS, - ENTITY_TYPE, - SERVICE_ENVIRONMENT, - ServiceOverviewParams, -} from '@kbn/observability-shared-plugin/common'; import React, { useCallback } from 'react'; -import { Entity } from '../../../../common/entities'; +import { ENTITY_DISPLAY_NAME } from '@kbn/observability-shared-plugin/common'; import { useKibana } from '../../../hooks/use_kibana'; +import type { Entity } from '../../../../common/entities'; import { EntityIcon } from '../../entity_icon'; +import { useDetailViewRedirect } from '../../../hooks/use_detail_view_redirect'; interface EntityNameProps { entity: Entity; @@ -26,14 +19,12 @@ interface EntityNameProps { export function EntityName({ entity }: EntityNameProps) { const { - services: { telemetry, share }, + services: { telemetry }, } = useKibana(); - const assetDetailsLocator = - share?.url.locators.get(ASSET_DETAILS_LOCATOR_ID); + const { getEntityRedirectUrl } = useDetailViewRedirect(); - const serviceOverviewLocator = - share?.url.locators.get('serviceOverviewLocator'); + const href = getEntityRedirectUrl(entity); const handleLinkClick = useCallback(() => { telemetry.reportEntityViewClicked({ @@ -42,47 +33,25 @@ export function EntityName({ entity }: EntityNameProps) { }); }, [entity, telemetry]); - const getEntityRedirectUrl = useCallback(() => { - const type = entity[ENTITY_TYPE]; - // For service, host and container type there is only one identity field - const identityField = Array.isArray(entity[ENTITY_IDENTITY_FIELDS]) - ? entity[ENTITY_IDENTITY_FIELDS][0] - : entity[ENTITY_IDENTITY_FIELDS]; - const identityValue = entity[identityField]; - - switch (type) { - case 'host': - case 'container': - return assetDetailsLocator?.getRedirectUrl({ - assetId: identityValue, - assetType: type, - }); - - case 'service': - return serviceOverviewLocator?.getRedirectUrl({ - serviceName: identityValue, - environment: [entity[SERVICE_ENVIRONMENT] || undefined].flat()[0], - }); - } - }, [entity, assetDetailsLocator, serviceOverviewLocator]); + const entityName = ( + + + + + + + {entity[ENTITY_DISPLAY_NAME]} + + + + ); - return ( + return href ? ( // eslint-disable-next-line @elastic/eui/href-or-on-click - - - - - - - - {entity[ENTITY_DISPLAY_NAME]} - - - + + {entityName} + ) : ( + entityName ); } diff --git a/x-pack/plugins/observability_solution/inventory/public/components/entity_icon/index.tsx b/x-pack/plugins/observability_solution/inventory/public/components/entity_icon/index.tsx index a62f0026ddfa0..48b21779d2e38 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/entity_icon/index.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/entity_icon/index.tsx @@ -6,7 +6,12 @@ */ import React from 'react'; -import { AGENT_NAME, CLOUD_PROVIDER, ENTITY_TYPE } from '@kbn/observability-shared-plugin/common'; +import { + AGENT_NAME, + CLOUD_PROVIDER, + ENTITY_TYPE, + ENTITY_TYPES, +} from '@kbn/observability-shared-plugin/common'; import { type CloudProvider, CloudProviderIcon, AgentIcon } from '@kbn/custom-icons'; import { EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; import type { AgentName } from '@kbn/elastic-agent-utils'; @@ -27,7 +32,7 @@ export function EntityIcon({ entity }: EntityIconProps) { const entityType = entity[ENTITY_TYPE]; const defaultIconSize = euiThemeVars.euiSizeL; - if (entityType === 'host' || entityType === 'container') { + if (entityType === ENTITY_TYPES.HOST || entityType === ENTITY_TYPES.CONTAINER) { const cloudProvider = getSingleValue( entity[CLOUD_PROVIDER] as NotNullableCloudProvider | NotNullableCloudProvider[] ); @@ -49,7 +54,7 @@ export function EntityIcon({ entity }: EntityIconProps) { ); } - if (entityType === 'service') { + if (entityType === ENTITY_TYPES.SERVICE) { const agentName = getSingleValue(entity[AGENT_NAME] as AgentName | AgentName[]); return ; } diff --git a/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/group_selector.test.tsx b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/group_selector.test.tsx new file mode 100644 index 0000000000000..23cbb5b43c43b --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/group_selector.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 { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { GroupSelector } from './group_selector'; + +import { InventoryComponentWrapperMock } from './mock/inventory_component_wrapper_mock'; + +describe('GroupSelector', () => { + beforeEach(() => { + render( + + + + ); + }); + it('Should default to Type', async () => { + expect(await screen.findByText('Group entities by: Type')).toBeInTheDocument(); + }); + + it.skip('Should change to None', async () => { + const user = userEvent.setup(); + + const selector = screen.getByText('Group entities by: Type'); + + expect(selector).toBeInTheDocument(); + + await user.click(selector); + + const noneOption = screen.getByTestId('panelUnified'); + + expect(noneOption).toBeInTheDocument(); + + await user.click(noneOption); + + expect(await screen.findByText('Group entities by: None')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/group_selector.tsx b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/group_selector.tsx new file mode 100644 index 0000000000000..95264f3c81303 --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/group_selector.tsx @@ -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 { EuiPopover, EuiContextMenu, EuiButtonEmpty } from '@elastic/eui'; +import React, { useCallback, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import type { EntityView } from '../../../common/entities'; +import { useInventoryParams } from '../../hooks/use_inventory_params'; +import { useInventoryRouter } from '../../hooks/use_inventory_router'; + +const GROUP_LABELS: Record = { + unified: i18n.translate('xpack.inventory.groupedInventoryPage.noneLabel', { + defaultMessage: 'None', + }), + grouped: i18n.translate('xpack.inventory.groupedInventoryPage.typeLabel', { + defaultMessage: 'Type', + }), +}; + +export interface GroupedSelectorProps { + groupSelected: string; + onGroupChange: (groupSelection: string) => void; +} + +export function GroupSelector() { + const { query } = useInventoryParams('/'); + const inventoryRoute = useInventoryRouter(); + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const groupBy = query.view ?? 'grouped'; + + const onGroupChange = (selected: EntityView) => { + const { pagination: _, ...rest } = query; + + inventoryRoute.push('/', { + path: {}, + query: { + ...rest, + view: groupBy === selected ? 'unified' : selected, + }, + }); + }; + + const isGroupSelected = (groupKey: EntityView) => { + return groupBy === groupKey; + }; + + const panels = [ + { + id: 'firstPanel', + title: i18n.translate('xpack.inventory.groupedInventoryPage.groupSelectorLabel', { + defaultMessage: 'Select grouping', + }), + items: [ + { + 'data-test-subj': 'panelUnified', + name: GROUP_LABELS.unified, + icon: isGroupSelected('unified') ? 'check' : 'empty', + onClick: () => onGroupChange('unified'), + }, + { + 'data-test-subj': 'panelType', + name: GROUP_LABELS.grouped, + icon: isGroupSelected('grouped') ? 'check' : 'empty', + onClick: () => onGroupChange('grouped'), + }, + ], + }, + ]; + + const onButtonClick = useCallback(() => setIsPopoverOpen((currentVal) => !currentVal), []); + + const closePopover = useCallback(() => setIsPopoverOpen(false), []); + + const button = ( + + + + ); + + return ( + + + + ); +} diff --git a/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/grouped_entities_grid.tsx b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/grouped_entities_grid.tsx new file mode 100644 index 0000000000000..d005a001999d5 --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/grouped_entities_grid.tsx @@ -0,0 +1,130 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { EuiDataGridSorting } from '@elastic/eui'; +import useEffectOnce from 'react-use/lib/useEffectOnce'; +import { decodeOrThrow } from '@kbn/io-ts-utils'; +import { useInventorySearchBarContext } from '../../context/inventory_search_bar_context_provider'; +import { useKibana } from '../../hooks/use_kibana'; +import { EntitiesGrid } from '../entities_grid'; +import { + entityPaginationRt, + type EntityColumnIds, + type EntityPagination, +} from '../../../common/entities'; +import { useInventoryAbortableAsync } from '../../hooks/use_inventory_abortable_async'; +import { useInventoryParams } from '../../hooks/use_inventory_params'; +import { useInventoryRouter } from '../../hooks/use_inventory_router'; + +interface Props { + field: string; +} + +const paginationDecoder = decodeOrThrow(entityPaginationRt); + +export function GroupedEntitiesGrid({ field }: Props) { + const { query } = useInventoryParams('/'); + const { sortField, sortDirection, kuery, pagination: paginationQuery } = query; + const inventoryRoute = useInventoryRouter(); + let pagination: EntityPagination | undefined = {}; + try { + pagination = paginationDecoder(paginationQuery); + } catch (error) { + inventoryRoute.push('/', { + path: {}, + query: { + sortField, + sortDirection, + kuery, + pagination: undefined, + }, + }); + window.location.reload(); + } + const pageIndex = pagination?.[field] ?? 0; + + const { refreshSubject$ } = useInventorySearchBarContext(); + const { + services: { inventoryAPIClient }, + } = useKibana(); + + const { + value = { entities: [] }, + loading, + refresh, + } = useInventoryAbortableAsync( + ({ signal }) => { + return inventoryAPIClient.fetch('GET /internal/inventory/entities', { + params: { + query: { + sortDirection, + sortField, + entityTypes: field?.length ? JSON.stringify([field]) : undefined, + kuery, + }, + }, + signal, + }); + }, + [field, inventoryAPIClient, kuery, sortDirection, sortField] + ); + + useEffectOnce(() => { + const refreshSubscription = refreshSubject$.subscribe(refresh); + + return () => refreshSubscription.unsubscribe(); + }); + + function handlePageChange(nextPage: number) { + inventoryRoute.push('/', { + path: {}, + query: { + ...query, + pagination: entityPaginationRt.encode({ + ...pagination, + [field]: nextPage, + }), + }, + }); + } + + function handleSortChange(sorting: EuiDataGridSorting['columns'][0]) { + inventoryRoute.push('/', { + path: {}, + query: { + ...query, + sortField: sorting.id as EntityColumnIds, + sortDirection: sorting.direction, + }, + }); + } + + function handleTypeFilter(type: string) { + const { pagination: _, ...rest } = query; + inventoryRoute.push('/', { + path: {}, + query: { + ...rest, + // Override the current entity types + entityTypes: [type], + }, + }); + } + + return ( + + ); +} diff --git a/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/index.tsx b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/index.tsx new file mode 100644 index 0000000000000..b376200495e43 --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/index.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { EuiSpacer } from '@elastic/eui'; +import { ENTITY_TYPE } from '@kbn/observability-shared-plugin/common'; +import React from 'react'; +import useEffectOnce from 'react-use/lib/useEffectOnce'; +import { InventoryGroupAccordion } from './inventory_group_accordion'; +import { useInventoryAbortableAsync } from '../../hooks/use_inventory_abortable_async'; +import { useKibana } from '../../hooks/use_kibana'; +import { InventorySummary } from './inventory_summary'; +import { useInventoryParams } from '../../hooks/use_inventory_params'; +import { useInventorySearchBarContext } from '../../context/inventory_search_bar_context_provider'; + +export function GroupedInventory() { + const { + services: { inventoryAPIClient }, + } = useKibana(); + const { query } = useInventoryParams('/'); + const { kuery, entityTypes } = query; + const { refreshSubject$ } = useInventorySearchBarContext(); + + const { + value = { groupBy: ENTITY_TYPE, groups: [], entitiesCount: 0 }, + refresh, + loading, + } = useInventoryAbortableAsync( + ({ signal }) => { + return inventoryAPIClient.fetch('GET /internal/inventory/entities/group_by/{field}', { + params: { + path: { + field: ENTITY_TYPE, + }, + query: { + kuery, + entityTypes: entityTypes?.length ? JSON.stringify(entityTypes) : undefined, + }, + }, + signal, + }); + }, + [entityTypes, inventoryAPIClient, kuery] + ); + + useEffectOnce(() => { + const refreshSubscription = refreshSubject$.subscribe(refresh); + + return () => refreshSubscription.unsubscribe(); + }); + + return ( + <> + + + {value.groups.map((group) => ( + + ))} + + ); +} diff --git a/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_group_accordion.test.tsx b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_group_accordion.test.tsx new file mode 100644 index 0000000000000..2cddbb8e46d79 --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_group_accordion.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 React from 'react'; +import { render, screen, within } from '@testing-library/react'; + +import { InventoryGroupAccordion } from './inventory_group_accordion'; + +describe('Grouped Inventory Accordion', () => { + it('renders with correct values', () => { + const props = { + groupBy: 'entity.type', + groups: [ + { + count: 5999, + 'entity.type': 'host', + }, + { + count: 2001, + 'entity.type': 'service', + }, + ], + }; + render(); + expect(screen.getByText(props.groups[0]['entity.type'])).toBeInTheDocument(); + const container = screen.getByTestId('inventoryPanelBadgeEntitiesCount_entity.type_host'); + expect(within(container).getByText('Entities:')).toBeInTheDocument(); + expect(within(container).getByText(props.groups[0].count)).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_group_accordion.tsx b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_group_accordion.tsx new file mode 100644 index 0000000000000..4c5d34e5a028f --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_group_accordion.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, { useCallback, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; +import { EuiAccordion, EuiPanel, EuiSpacer, EuiTitle, useEuiTheme } from '@elastic/eui'; +import { GroupedEntitiesGrid } from './grouped_entities_grid'; +import type { EntityGroup } from '../../../common/entities'; +import { InventoryPanelBadge } from './inventory_panel_badge'; + +const ENTITIES_COUNT_BADGE = i18n.translate( + 'xpack.inventory.inventoryGroupPanel.entitiesBadgeLabel', + { defaultMessage: 'Entities' } +); + +export interface InventoryGroupAccordionProps { + group: EntityGroup; + groupBy: string; + isLoading?: boolean; +} + +export function InventoryGroupAccordion({ + group, + groupBy, + isLoading, +}: InventoryGroupAccordionProps) { + const { euiTheme } = useEuiTheme(); + const field = group[groupBy]; + const [open, setOpen] = useState(false); + + const onToggle = useCallback(() => { + setOpen((opened) => !opened); + }, []); + + return ( + <> + + +

{field}

+ + } + buttonElement="div" + extraAction={ + + } + buttonProps={{ paddingSize: 'm' }} + paddingSize="none" + onToggle={onToggle} + isLoading={isLoading} + /> +
+ {open && ( + + + + )} + + + ); +} diff --git a/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_panel_badge.tsx b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_panel_badge.tsx new file mode 100644 index 0000000000000..43db1c39154bc --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_panel_badge.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 { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import React from 'react'; + +export function InventoryPanelBadge({ + name, + value, + 'data-test-subj': dataTestSubj, +}: { + name: string; + 'data-test-subj'?: string; + value: string | number; +}) { + return ( + + + + {name}: + + + + {value} + + + ); +} diff --git a/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_summary.test.tsx b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_summary.test.tsx new file mode 100644 index 0000000000000..63583e60b0edd --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_summary.test.tsx @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { EuiThemeProvider } from '@elastic/eui'; +import { I18nProvider } from '@kbn/i18n-react'; +import { InventorySummary } from './inventory_summary'; + +// Do not test the GroupSelector, as it needs a lot more complicated setup +jest.mock('./group_selector', () => ({ + GroupSelector: () => <>Selector, +})); + +function MockEnvWrapper({ children }: { children?: React.ReactNode }) { + return ( + + {children} + + ); +} + +describe('InventorySummary', () => { + it('renders the total entities without any group totals', () => { + render(, { wrapper: MockEnvWrapper }); + expect(screen.getByText('10 Entities')).toBeInTheDocument(); + expect(screen.queryByTestId('inventorySummaryGroupsTotal')).not.toBeInTheDocument(); + }); + it('renders the total entities with group totals', () => { + render(, { wrapper: MockEnvWrapper }); + expect(screen.getByText('15 Entities')).toBeInTheDocument(); + expect(screen.queryByText('3 Groups')).toBeInTheDocument(); + }); + it("won't render either totals when not provided anything", () => { + render(, { wrapper: MockEnvWrapper }); + expect(screen.queryByTestId('inventorySummaryEntitiesTotal')).not.toBeInTheDocument(); + expect(screen.queryByTestId('inventorySummaryGroupsTotal')).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_summary.tsx b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_summary.tsx new file mode 100644 index 0000000000000..55697790c4ee9 --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_summary.tsx @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem, useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { GroupSelector } from './group_selector'; + +export function InventorySummary({ + totalEntities, + totalGroups, +}: { + totalEntities?: number; + totalGroups?: number; +}) { + const { euiTheme } = useEuiTheme(); + + const isGrouped = totalGroups !== undefined; + + return ( + + + + {totalEntities !== undefined && ( + + + + + + )} + {isGrouped ? ( + + + + + + ) : null} + + + + + + + ); +} diff --git a/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/mock/inventory_component_wrapper_mock.tsx b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/mock/inventory_component_wrapper_mock.tsx new file mode 100644 index 0000000000000..08c8e93aadda8 --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/mock/inventory_component_wrapper_mock.tsx @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { CoreStart } from '@kbn/core/public'; +import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; +import { createMemoryHistory } from 'history'; +import { RouterProvider } from '@kbn/typed-react-router-config'; +import { I18nProvider } from '@kbn/i18n-react'; +import { EuiThemeProvider } from '@elastic/eui'; +import { getMockInventoryContext } from '../../../../.storybook/get_mock_inventory_context'; +import { inventoryRouter } from '../../../routes/config'; +import { InventoryContextProvider } from '../../../context/inventory_context_provider'; + +export function InventoryComponentWrapperMock({ children }: React.PropsWithChildren<{}>) { + const context = getMockInventoryContext(); + const KibanaReactContext = createKibanaReactContext(context as unknown as Partial); + const history = createMemoryHistory({ + initialEntries: ['/'], + }); + return ( + + + + + + {children} + + + + + + ); +} diff --git a/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/unified_inventory.tsx b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/unified_inventory.tsx new file mode 100644 index 0000000000000..05f7437a32c4b --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/unified_inventory.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { EuiDataGridSorting } from '@elastic/eui'; +import React from 'react'; +import useEffectOnce from 'react-use/lib/useEffectOnce'; +import { decodeOrThrow } from '@kbn/io-ts-utils'; +import { + type EntityColumnIds, + entityPaginationRt, + type EntityPagination, +} from '../../../common/entities'; +import { EntitiesGrid } from '../entities_grid'; +import { useInventoryAbortableAsync } from '../../hooks/use_inventory_abortable_async'; +import { useInventoryParams } from '../../hooks/use_inventory_params'; +import { useInventoryRouter } from '../../hooks/use_inventory_router'; +import { useKibana } from '../../hooks/use_kibana'; +import { useInventorySearchBarContext } from '../../context/inventory_search_bar_context_provider'; +import { InventorySummary } from './inventory_summary'; + +const paginationDecoder = decodeOrThrow(entityPaginationRt); + +export function UnifiedInventory() { + const { + services: { inventoryAPIClient }, + } = useKibana(); + const { refreshSubject$ } = useInventorySearchBarContext(); + const { query } = useInventoryParams('/'); + const { sortDirection, sortField, kuery, entityTypes, pagination: paginationQuery } = query; + let pagination: EntityPagination | undefined = {}; + const inventoryRoute = useInventoryRouter(); + try { + pagination = paginationDecoder(paginationQuery); + } catch (error) { + inventoryRoute.push('/', { + path: {}, + query: { + sortField, + sortDirection, + kuery, + pagination: undefined, + }, + }); + window.location.reload(); + } + + const pageIndex = pagination?.unified ?? 0; + + const { + value = { entities: [] }, + loading, + refresh, + } = useInventoryAbortableAsync( + ({ signal }) => { + return inventoryAPIClient.fetch('GET /internal/inventory/entities', { + params: { + query: { + sortDirection, + sortField, + entityTypes: entityTypes?.length ? JSON.stringify(entityTypes) : undefined, + kuery, + }, + }, + signal, + }); + }, + [entityTypes, inventoryAPIClient, kuery, sortDirection, sortField] + ); + + useEffectOnce(() => { + const refreshSubscription = refreshSubject$.subscribe(refresh); + + return () => refreshSubscription.unsubscribe(); + }); + + function handlePageChange(nextPage: number) { + inventoryRoute.push('/', { + path: {}, + query: { + ...query, + pagination: entityPaginationRt.encode({ + ...pagination, + unified: nextPage, + }), + }, + }); + } + + function handleSortChange(sorting: EuiDataGridSorting['columns'][0]) { + inventoryRoute.push('/', { + path: {}, + query: { + ...query, + sortField: sorting.id as EntityColumnIds, + sortDirection: sorting.direction, + }, + }); + } + + function handleTypeFilter(type: string) { + const { pagination: _, ...rest } = query; + + inventoryRoute.push('/', { + path: {}, + query: { + ...rest, + // Override the current entity types + entityTypes: [type], + }, + }); + } + + return ( + <> + + + + ); +} diff --git a/x-pack/plugins/observability_solution/inventory/public/components/search_bar/discover_button.tsx b/x-pack/plugins/observability_solution/inventory/public/components/search_bar/discover_button.tsx index dee05d6f7cdd0..d5ed5b5af8cf9 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/search_bar/discover_button.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/search_bar/discover_button.tsx @@ -17,7 +17,7 @@ import { ENTITY_LAST_SEEN, ENTITY_TYPE, } from '@kbn/observability-shared-plugin/common'; -import { ENTITIES_LATEST_ALIAS, EntityColumnIds } from '../../../common/entities'; +import { EntityColumnIds } from '../../../common/entities'; import { useInventoryParams } from '../../hooks/use_inventory_params'; import { useKibana } from '../../hooks/use_kibana'; @@ -38,17 +38,6 @@ export function DiscoverButton({ dataView }: { dataView: DataView }) { const filters: PhrasesFilter[] = []; - const entityDefinitionField = dataView.getFieldByName(ENTITY_DEFINITION_ID); - - if (entityDefinitionField) { - const entityDefinitionFilter = buildPhrasesFilter( - entityDefinitionField!, - [ENTITIES_LATEST_ALIAS], - dataView - ); - filters.push(entityDefinitionFilter); - } - const entityTypeField = dataView.getFieldByName(ENTITY_TYPE); if (entityTypes && entityTypeField) { @@ -56,10 +45,14 @@ export function DiscoverButton({ dataView }: { dataView: DataView }) { filters.push(entityTypeFilter); } + const kueryWithEntityDefinitionFilters = [kuery, `${ENTITY_DEFINITION_ID} : builtin*`] + .filter(Boolean) + .join(' AND '); + const discoverLink = discoverLocator?.getRedirectUrl({ indexPatternId: dataView?.id ?? '', columns: ACTIVE_COLUMNS, - query: { query: kuery ?? '', language: 'kuery' }, + query: { query: kueryWithEntityDefinitionFilters, language: 'kuery' }, filters, }); diff --git a/x-pack/plugins/observability_solution/inventory/public/components/search_bar/index.tsx b/x-pack/plugins/observability_solution/inventory/public/components/search_bar/index.tsx index 4e945dd9a1cad..2fd450aab30dd 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/search_bar/index.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/search_bar/index.tsx @@ -19,7 +19,7 @@ import { DiscoverButton } from './discover_button'; import { getKqlFieldsWithFallback } from '../../utils/get_kql_field_names_with_fallback'; export function SearchBar() { - const { searchBarContentSubject$ } = useInventorySearchBarContext(); + const { searchBarContentSubject$, refreshSubject$ } = useInventorySearchBarContext(); const { services: { unifiedSearch, @@ -84,7 +84,7 @@ export function SearchBar() { const handleEntityTypesChange = useCallback( (nextEntityTypes: string[]) => { - searchBarContentSubject$.next({ kuery, entityTypes: nextEntityTypes, refresh: false }); + searchBarContentSubject$.next({ kuery, entityTypes: nextEntityTypes }); registerEntityTypeFilteredEvent({ filterEntityTypes: nextEntityTypes, filterKuery: kuery }); }, [kuery, registerEntityTypeFilteredEvent, searchBarContentSubject$] @@ -95,7 +95,6 @@ export function SearchBar() { searchBarContentSubject$.next({ kuery: query?.query as string, entityTypes, - refresh: !isUpdate, }); registerSearchSubmittedEvent({ @@ -103,8 +102,12 @@ export function SearchBar() { searchEntityTypes: entityTypes, searchIsUpdate: isUpdate, }); + + if (!isUpdate) { + refreshSubject$.next(); + } }, - [entityTypes, registerSearchSubmittedEvent, searchBarContentSubject$] + [entityTypes, registerSearchSubmittedEvent, searchBarContentSubject$, refreshSubject$] ); return ( diff --git a/x-pack/plugins/observability_solution/inventory/public/context/inventory_search_bar_context_provider/index.tsx b/x-pack/plugins/observability_solution/inventory/public/context/inventory_search_bar_context_provider/index.tsx index fbb51c4f0d7e7..eb5a2a057e529 100644 --- a/x-pack/plugins/observability_solution/inventory/public/context/inventory_search_bar_context_provider/index.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/context/inventory_search_bar_context_provider/index.tsx @@ -11,17 +11,20 @@ interface InventorySearchBarContextType { searchBarContentSubject$: Subject<{ kuery?: string; entityTypes?: string[]; - refresh: boolean; }>; + refreshSubject$: Subject; } const InventorySearchBarContext = createContext({ searchBarContentSubject$: new Subject(), + refreshSubject$: new Subject(), }); export function InventorySearchBarContextProvider({ children }: { children: ReactChild }) { return ( - + {children} ); diff --git a/x-pack/plugins/observability_solution/inventory/public/hooks/use_detail_view_redirect.test.ts b/x-pack/plugins/observability_solution/inventory/public/hooks/use_detail_view_redirect.test.ts new file mode 100644 index 0000000000000..cf4993f871880 --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/hooks/use_detail_view_redirect.test.ts @@ -0,0 +1,170 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { useDetailViewRedirect } from './use_detail_view_redirect'; +import { useKibana } from './use_kibana'; +import { + AGENT_NAME, + CLOUD_PROVIDER, + CONTAINER_ID, + ENTITY_DEFINITION_ID, + ENTITY_DISPLAY_NAME, + ENTITY_ID, + ENTITY_IDENTITY_FIELDS, + ENTITY_LAST_SEEN, + ENTITY_TYPE, + HOST_NAME, + ENTITY_TYPES, + SERVICE_ENVIRONMENT, + SERVICE_NAME, +} from '@kbn/observability-shared-plugin/common'; +import { unflattenEntity } from '../../common/utils/unflatten_entity'; +import type { Entity } from '../../common/entities'; + +jest.mock('./use_kibana'); +jest.mock('../../common/utils/unflatten_entity'); + +const useKibanaMock = useKibana as jest.Mock; +const unflattenEntityMock = unflattenEntity as jest.Mock; + +const commonEntityFields: Partial = { + [ENTITY_LAST_SEEN]: '2023-10-09T00:00:00Z', + [ENTITY_ID]: '1', + [ENTITY_DISPLAY_NAME]: 'entity_name', + [ENTITY_DEFINITION_ID]: 'entity_definition_id', +}; + +describe('useDetailViewRedirect', () => { + const mockGetIdentityFieldsValue = jest.fn(); + const mockAsKqlFilter = jest.fn(); + const mockGetRedirectUrl = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + + useKibanaMock.mockReturnValue({ + services: { + share: { + url: { + locators: { + get: jest.fn().mockReturnValue({ + getRedirectUrl: mockGetRedirectUrl, + }), + }, + }, + }, + entityManager: { + entityClient: { + getIdentityFieldsValue: mockGetIdentityFieldsValue, + asKqlFilter: mockAsKqlFilter, + }, + }, + }, + }); + + unflattenEntityMock.mockImplementation((entity) => entity); + }); + + it('getEntityRedirectUrl should return the correct URL for host entity', () => { + const entity: Entity = { + ...(commonEntityFields as Entity), + [ENTITY_IDENTITY_FIELDS]: [HOST_NAME], + [ENTITY_TYPE]: 'host', + [HOST_NAME]: 'host-1', + [CLOUD_PROVIDER]: null, + }; + + mockGetIdentityFieldsValue.mockReturnValue({ [HOST_NAME]: 'host-1' }); + mockGetRedirectUrl.mockReturnValue('asset-details-url'); + + const { result } = renderHook(() => useDetailViewRedirect()); + const url = result.current.getEntityRedirectUrl(entity); + + expect(url).toBe('asset-details-url'); + expect(mockGetRedirectUrl).toHaveBeenCalledWith({ assetId: 'host-1', assetType: 'host' }); + }); + + it('getEntityRedirectUrl should return the correct URL for container entity', () => { + const entity: Entity = { + ...(commonEntityFields as Entity), + [ENTITY_IDENTITY_FIELDS]: [CONTAINER_ID], + [ENTITY_TYPE]: 'container', + [CONTAINER_ID]: 'container-1', + [CLOUD_PROVIDER]: null, + }; + + mockGetIdentityFieldsValue.mockReturnValue({ [CONTAINER_ID]: 'container-1' }); + mockGetRedirectUrl.mockReturnValue('asset-details-url'); + + const { result } = renderHook(() => useDetailViewRedirect()); + const url = result.current.getEntityRedirectUrl(entity); + + expect(url).toBe('asset-details-url'); + expect(mockGetRedirectUrl).toHaveBeenCalledWith({ + assetId: 'container-1', + assetType: 'container', + }); + }); + + it('getEntityRedirectUrl should return the correct URL for service entity', () => { + const entity: Entity = { + ...(commonEntityFields as Entity), + [ENTITY_IDENTITY_FIELDS]: [SERVICE_NAME], + [ENTITY_TYPE]: 'service', + [SERVICE_NAME]: 'service-1', + [SERVICE_ENVIRONMENT]: 'prod', + [AGENT_NAME]: 'node', + }; + mockGetIdentityFieldsValue.mockReturnValue({ [SERVICE_NAME]: 'service-1' }); + mockGetRedirectUrl.mockReturnValue('service-overview-url'); + + const { result } = renderHook(() => useDetailViewRedirect()); + const url = result.current.getEntityRedirectUrl(entity); + + expect(url).toBe('service-overview-url'); + expect(mockGetRedirectUrl).toHaveBeenCalledWith({ + serviceName: 'service-1', + environment: 'prod', + }); + }); + + [ + [ENTITY_TYPES.KUBERNETES.CLUSTER.ecs, 'kubernetes-f4dc26db-1b53-4ea2-a78b-1bfab8ea267c'], + [ENTITY_TYPES.KUBERNETES.CLUSTER.semconv, 'kubernetes_otel-cluster-overview'], + [ENTITY_TYPES.KUBERNETES.CRONJOB.ecs, 'kubernetes-0a672d50-bcb1-11ec-b64f-7dd6e8e82013'], + [ENTITY_TYPES.KUBERNETES.DAEMONSET.ecs, 'kubernetes-85879010-bcb1-11ec-b64f-7dd6e8e82013'], + [ENTITY_TYPES.KUBERNETES.DEPLOYMENT.ecs, 'kubernetes-5be46210-bcb1-11ec-b64f-7dd6e8e82013'], + [ENTITY_TYPES.KUBERNETES.JOB.ecs, 'kubernetes-9bf990a0-bcb1-11ec-b64f-7dd6e8e82013'], + [ENTITY_TYPES.KUBERNETES.NODE.ecs, 'kubernetes-b945b7b0-bcb1-11ec-b64f-7dd6e8e82013'], + [ENTITY_TYPES.KUBERNETES.POD.ecs, 'kubernetes-3d4d9290-bcb1-11ec-b64f-7dd6e8e82013'], + [ENTITY_TYPES.KUBERNETES.STATEFULSET.ecs, 'kubernetes-21694370-bcb2-11ec-b64f-7dd6e8e82013'], + ].forEach(([entityType, dashboardId]) => { + it(`getEntityRedirectUrl should return the correct URL for ${entityType} entity`, () => { + const entity: Entity = { + ...(commonEntityFields as Entity), + [ENTITY_IDENTITY_FIELDS]: ['some.field'], + [ENTITY_TYPE]: entityType, + }; + + mockAsKqlFilter.mockReturnValue('kql-query'); + mockGetRedirectUrl.mockReturnValue('dashboard-url'); + + const { result } = renderHook(() => useDetailViewRedirect()); + const url = result.current.getEntityRedirectUrl(entity); + + expect(url).toBe('dashboard-url'); + expect(mockGetRedirectUrl).toHaveBeenCalledWith({ + dashboardId, + query: { + language: 'kuery', + query: 'kql-query', + }, + }); + }); + }); +}); diff --git a/x-pack/plugins/observability_solution/inventory/public/hooks/use_detail_view_redirect.ts b/x-pack/plugins/observability_solution/inventory/public/hooks/use_detail_view_redirect.ts new file mode 100644 index 0000000000000..23380dc3704de --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/hooks/use_detail_view_redirect.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 { + ASSET_DETAILS_LOCATOR_ID, + AssetDetailsLocatorParams, + ENTITY_IDENTITY_FIELDS, + ENTITY_TYPE, + ENTITY_TYPES, + SERVICE_ENVIRONMENT, + SERVICE_OVERVIEW_LOCATOR_ID, + ServiceOverviewParams, +} from '@kbn/observability-shared-plugin/common'; +import { useCallback } from 'react'; +import { DashboardLocatorParams } from '@kbn/dashboard-plugin/public'; +import { DASHBOARD_APP_LOCATOR } from '@kbn/deeplinks-analytics'; +import { castArray } from 'lodash'; +import type { Entity } from '../../common/entities'; +import { unflattenEntity } from '../../common/utils/unflatten_entity'; +import { useKibana } from './use_kibana'; + +const KUBERNETES_DASHBOARDS_IDS: Record = { + [ENTITY_TYPES.KUBERNETES.CLUSTER.ecs]: 'kubernetes-f4dc26db-1b53-4ea2-a78b-1bfab8ea267c', + [ENTITY_TYPES.KUBERNETES.CLUSTER.semconv]: 'kubernetes_otel-cluster-overview', + [ENTITY_TYPES.KUBERNETES.CRONJOB.ecs]: 'kubernetes-0a672d50-bcb1-11ec-b64f-7dd6e8e82013', + [ENTITY_TYPES.KUBERNETES.DAEMONSET.ecs]: 'kubernetes-85879010-bcb1-11ec-b64f-7dd6e8e82013', + [ENTITY_TYPES.KUBERNETES.DEPLOYMENT.ecs]: 'kubernetes-5be46210-bcb1-11ec-b64f-7dd6e8e82013', + [ENTITY_TYPES.KUBERNETES.JOB.ecs]: 'kubernetes-9bf990a0-bcb1-11ec-b64f-7dd6e8e82013', + [ENTITY_TYPES.KUBERNETES.NODE.ecs]: 'kubernetes-b945b7b0-bcb1-11ec-b64f-7dd6e8e82013', + [ENTITY_TYPES.KUBERNETES.POD.ecs]: 'kubernetes-3d4d9290-bcb1-11ec-b64f-7dd6e8e82013', + [ENTITY_TYPES.KUBERNETES.STATEFULSET.ecs]: 'kubernetes-21694370-bcb2-11ec-b64f-7dd6e8e82013', +}; + +export const useDetailViewRedirect = () => { + const { + services: { share, entityManager }, + } = useKibana(); + + const locators = share.url.locators; + const assetDetailsLocator = locators.get(ASSET_DETAILS_LOCATOR_ID); + const dashboardLocator = locators.get(DASHBOARD_APP_LOCATOR); + const serviceOverviewLocator = locators.get(SERVICE_OVERVIEW_LOCATOR_ID); + + const getSingleIdentityFieldValue = useCallback( + (entity: Entity) => { + const identityFields = castArray(entity[ENTITY_IDENTITY_FIELDS]); + if (identityFields.length > 1) { + throw new Error(`Multiple identity fields are not supported for ${entity[ENTITY_TYPE]}`); + } + + const identityField = identityFields[0]; + return entityManager.entityClient.getIdentityFieldsValue(unflattenEntity(entity))[ + identityField + ]; + }, + [entityManager.entityClient] + ); + + const getDetailViewRedirectUrl = useCallback( + (entity: Entity) => { + const type = entity[ENTITY_TYPE]; + const identityValue = getSingleIdentityFieldValue(entity); + + switch (type) { + case ENTITY_TYPES.HOST: + case ENTITY_TYPES.CONTAINER: + return assetDetailsLocator?.getRedirectUrl({ + assetId: identityValue, + assetType: type, + }); + + case 'service': + return serviceOverviewLocator?.getRedirectUrl({ + serviceName: identityValue, + // service.environemnt is not part of entity.identityFields + // we need to manually get its value + environment: [entity[SERVICE_ENVIRONMENT] || undefined].flat()[0], + }); + + default: + return undefined; + } + }, + [assetDetailsLocator, getSingleIdentityFieldValue, serviceOverviewLocator] + ); + + const getDashboardRedirectUrl = useCallback( + (entity: Entity) => { + const type = entity[ENTITY_TYPE]; + const dashboardId = KUBERNETES_DASHBOARDS_IDS[type]; + + return dashboardId + ? dashboardLocator?.getRedirectUrl({ + dashboardId, + query: { + language: 'kuery', + query: entityManager.entityClient.asKqlFilter(unflattenEntity(entity)), + }, + }) + : undefined; + }, + [dashboardLocator, entityManager.entityClient] + ); + + const getEntityRedirectUrl = useCallback( + (entity: Entity) => getDetailViewRedirectUrl(entity) ?? getDashboardRedirectUrl(entity), + [getDashboardRedirectUrl, getDetailViewRedirectUrl] + ); + + return { getEntityRedirectUrl }; +}; diff --git a/x-pack/plugins/observability_solution/inventory/public/pages/inventory_page/index.tsx b/x-pack/plugins/observability_solution/inventory/public/pages/inventory_page/index.tsx index 00dfb9e24d2dd..03f8b6475175a 100644 --- a/x-pack/plugins/observability_solution/inventory/public/pages/inventory_page/index.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/pages/inventory_page/index.tsx @@ -4,105 +4,36 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiDataGridSorting } from '@elastic/eui'; -import React from 'react'; -import useEffectOnce from 'react-use/lib/useEffectOnce'; -import { EntityColumnIds } from '../../../common/entities'; -import { EntitiesGrid } from '../../components/entities_grid'; -import { useInventorySearchBarContext } from '../../context/inventory_search_bar_context_provider'; -import { useInventoryAbortableAsync } from '../../hooks/use_inventory_abortable_async'; +import React, { useEffect } from 'react'; import { useInventoryParams } from '../../hooks/use_inventory_params'; +import { useInventorySearchBarContext } from '../../context/inventory_search_bar_context_provider'; import { useInventoryRouter } from '../../hooks/use_inventory_router'; -import { useKibana } from '../../hooks/use_kibana'; +import { UnifiedInventory } from '../../components/grouped_inventory/unified_inventory'; +import { GroupedInventory } from '../../components/grouped_inventory'; export function InventoryPage() { const { searchBarContentSubject$ } = useInventorySearchBarContext(); - const { - services: { inventoryAPIClient }, - } = useKibana(); - const { query } = useInventoryParams('/'); - const { sortDirection, sortField, pageIndex, kuery, entityTypes } = query; - const inventoryRoute = useInventoryRouter(); + const { query } = useInventoryParams('/'); - const { - value = { entities: [] }, - loading, - refresh, - } = useInventoryAbortableAsync( - ({ signal }) => { - return inventoryAPIClient.fetch('GET /internal/inventory/entities', { - params: { - query: { - sortDirection, - sortField, - entityTypes: entityTypes?.length ? JSON.stringify(entityTypes) : undefined, - kuery, - }, - }, - signal, - }); - }, - [entityTypes, inventoryAPIClient, kuery, sortDirection, sortField] - ); - - useEffectOnce(() => { + useEffect(() => { const searchBarContentSubscription = searchBarContentSubject$.subscribe( - ({ refresh: isRefresh, ...queryParams }) => { - if (isRefresh) { - refresh(); - } else { - inventoryRoute.push('/', { - path: {}, - query: { ...query, ...queryParams }, - }); - } + ({ ...queryParams }) => { + const { pagination: _, ...rest } = query; + + inventoryRoute.push('/', { + path: {}, + query: { ...rest, ...queryParams }, + }); } ); return () => { searchBarContentSubscription.unsubscribe(); }; - }); - - function handlePageChange(nextPage: number) { - inventoryRoute.push('/', { - path: {}, - query: { ...query, pageIndex: nextPage }, - }); - } - - function handleSortChange(sorting: EuiDataGridSorting['columns'][0]) { - inventoryRoute.push('/', { - path: {}, - query: { - ...query, - sortField: sorting.id as EntityColumnIds, - sortDirection: sorting.direction, - }, - }); - } - - function handleTypeFilter(entityType: string) { - inventoryRoute.push('/', { - path: {}, - query: { - ...query, - // Override the current entity types - entityTypes: [entityType], - }, - }); - } + // If query has updated, the inventoryRoute state is also updated + // as well, so we only need to track changes on query. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [query, searchBarContentSubject$]); - return ( - - ); + return query.view === 'unified' ? : ; } diff --git a/x-pack/plugins/observability_solution/inventory/public/routes/config.tsx b/x-pack/plugins/observability_solution/inventory/public/routes/config.tsx index dc7ba13451e02..36a15c5ae542c 100644 --- a/x-pack/plugins/observability_solution/inventory/public/routes/config.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/routes/config.tsx @@ -4,13 +4,17 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { toNumberRt } from '@kbn/io-ts-utils'; import { Outlet, createRouter } from '@kbn/typed-react-router-config'; import * as t from 'io-ts'; import React from 'react'; import { InventoryPageTemplate } from '../components/inventory_page_template'; import { InventoryPage } from '../pages/inventory_page'; -import { defaultEntitySortField, entityTypesRt, entityColumnIdsRt } from '../../common/entities'; +import { + defaultEntitySortField, + entityTypesRt, + entityColumnIdsRt, + entityViewRt, +} from '../../common/entities'; /** * The array of route definitions to be used when the application @@ -28,11 +32,12 @@ const inventoryRoutes = { t.type({ sortField: entityColumnIdsRt, sortDirection: t.union([t.literal('asc'), t.literal('desc')]), - pageIndex: toNumberRt, }), t.partial({ entityTypes: entityTypesRt, kuery: t.string, + view: entityViewRt, + pagination: t.string, }), ]), }), @@ -40,7 +45,7 @@ const inventoryRoutes = { query: { sortField: defaultEntitySortField, sortDirection: 'desc', - pageIndex: '0', + view: 'grouped', }, }, children: { diff --git a/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_entity_groups.ts b/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_entity_groups.ts new file mode 100644 index 0000000000000..b61f245f1aaf2 --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_entity_groups.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 { ObservabilityElasticsearchClient } from '@kbn/observability-utils/es/client/create_observability_es_client'; +import { kqlQuery } from '@kbn/observability-utils/es/queries/kql_query'; +import { esqlResultToPlainObjects } from '@kbn/observability-utils/es/utils/esql_result_to_plain_objects'; +import { ENTITY_TYPE } from '@kbn/observability-shared-plugin/common'; +import { ScalarValue } from '@elastic/elasticsearch/lib/api/types'; +import { + ENTITIES_LATEST_ALIAS, + type EntityGroup, + MAX_NUMBER_OF_ENTITIES, +} from '../../../common/entities'; +import { getBuiltinEntityDefinitionIdESQLWhereClause } from './query_helper'; + +export async function getEntityGroupsBy({ + inventoryEsClient, + field, + kuery, + entityTypes, +}: { + inventoryEsClient: ObservabilityElasticsearchClient; + field: string; + kuery?: string; + entityTypes?: string[]; +}) { + const from = `FROM ${ENTITIES_LATEST_ALIAS}`; + const where = [getBuiltinEntityDefinitionIdESQLWhereClause()]; + const params: ScalarValue[] = []; + + if (entityTypes) { + where.push(`WHERE ${ENTITY_TYPE} IN (${entityTypes.map(() => '?').join()})`); + params.push(...entityTypes); + } + + // STATS doesn't support parameterisation. + const group = `STATS count = COUNT(*) by ${field}`; + const sort = `SORT ${field} asc`; + // LIMIT doesn't support parameterisation. + const limit = `LIMIT ${MAX_NUMBER_OF_ENTITIES}`; + const query = [from, ...where, group, sort, limit].join(' | '); + + const groups = await inventoryEsClient.esql('get_entities_groups', { + query, + filter: { + bool: { + filter: kqlQuery(kuery), + }, + }, + params, + }); + + return esqlResultToPlainObjects(groups); +} diff --git a/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_identify_fields.test.ts b/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_identify_fields.test.ts index ffd5ba9c6f855..62d77c08fd27a 100644 --- a/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_identify_fields.test.ts +++ b/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_identify_fields.test.ts @@ -30,7 +30,7 @@ describe('getIdentityFields', () => { it('should return a Map with unique entity types and their respective identity fields', () => { const serviceEntity: Entity = { 'agent.name': 'node', - 'entity.identity_fields': ['service.name', 'service.environment'], + [ENTITY_IDENTITY_FIELDS]: ['service.name', 'service.environment'], 'service.name': 'my-service', 'entity.type': 'service', ...commonEntityFields, diff --git a/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_latest_entities.ts b/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_latest_entities.ts index 4fb3b930beace..c95a488ad49dd 100644 --- a/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_latest_entities.ts +++ b/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_latest_entities.ts @@ -40,7 +40,7 @@ export async function getLatestEntities({ if (entityTypes) { where.push(`WHERE ${ENTITY_TYPE} IN (${entityTypes.map(() => '?').join()})`); - params.push(...entityTypes.map((entityType) => entityType)); + params.push(...entityTypes); } const sort = `SORT ${entitiesSortField} ${sortDirection}`; diff --git a/x-pack/plugins/observability_solution/inventory/server/routes/entities/route.ts b/x-pack/plugins/observability_solution/inventory/server/routes/entities/route.ts index 67b3803dd98de..88d6cb68ee214 100644 --- a/x-pack/plugins/observability_solution/inventory/server/routes/entities/route.ts +++ b/x-pack/plugins/observability_solution/inventory/server/routes/entities/route.ts @@ -7,6 +7,7 @@ import { INVENTORY_APP_ID } from '@kbn/deeplinks-observability/constants'; import { jsonRt } from '@kbn/io-ts-utils'; import { createObservabilityEsClient } from '@kbn/observability-utils/es/client/create_observability_es_client'; +import { ENTITY_TYPE } from '@kbn/observability-shared-plugin/common'; import * as t from 'io-ts'; import { orderBy } from 'lodash'; import { joinByKey } from '@kbn/observability-utils/array/join_by_key'; @@ -17,6 +18,7 @@ import { getLatestEntities } from './get_latest_entities'; import { createAlertsClient } from '../../lib/create_alerts_client.ts/create_alerts_client'; import { getLatestEntitiesAlerts } from './get_latest_entities_alerts'; import { getIdentityFieldsPerEntityType } from './get_identity_fields_per_entity_type'; +import { getEntityGroupsBy } from './get_entity_groups'; export const getEntityTypesRoute = createInventoryServerRoute({ endpoint: 'GET /internal/inventory/entities/types', @@ -106,7 +108,46 @@ export const listLatestEntitiesRoute = createInventoryServerRoute({ }, }); +export const groupEntitiesByRoute = createInventoryServerRoute({ + endpoint: 'GET /internal/inventory/entities/group_by/{field}', + params: t.intersection([ + t.type({ path: t.type({ field: t.literal(ENTITY_TYPE) }) }), + t.partial({ + query: t.partial({ + kuery: t.string, + entityTypes: jsonRt.pipe(t.array(t.string)), + }), + }), + ]), + options: { + tags: ['access:inventory'], + }, + handler: async ({ params, context, logger }) => { + const coreContext = await context.core; + const inventoryEsClient = createObservabilityEsClient({ + client: coreContext.elasticsearch.client.asCurrentUser, + logger, + plugin: `@kbn/${INVENTORY_APP_ID}-plugin`, + }); + + const { field } = params.path; + const { kuery, entityTypes } = params.query ?? {}; + + const groups = await getEntityGroupsBy({ + inventoryEsClient, + field, + kuery, + entityTypes, + }); + + const entitiesCount = groups.reduce((acc, group) => acc + group.count, 0); + + return { groupBy: field, groups, entitiesCount }; + }, +}); + export const entitiesRoutes = { ...listLatestEntitiesRoute, ...getEntityTypesRoute, + ...groupEntitiesByRoute, }; diff --git a/x-pack/plugins/observability_solution/inventory/server/routes/has_data/get_has_data.ts b/x-pack/plugins/observability_solution/inventory/server/routes/has_data/get_has_data.ts index 27ba8c0fe46c3..c1e4a82c343b0 100644 --- a/x-pack/plugins/observability_solution/inventory/server/routes/has_data/get_has_data.ts +++ b/x-pack/plugins/observability_solution/inventory/server/routes/has_data/get_has_data.ts @@ -26,7 +26,6 @@ export async function getHasData({ }); const totalCount = esqlResultToPlainObjects(esqlResults)?.[0]._count ?? 0; - return { hasData: totalCount > 0 }; } catch (e) { logger.error(e); diff --git a/x-pack/plugins/observability_solution/inventory/tsconfig.json b/x-pack/plugins/observability_solution/inventory/tsconfig.json index 67de9919c6324..bd77df478cad1 100644 --- a/x-pack/plugins/observability_solution/inventory/tsconfig.json +++ b/x-pack/plugins/observability_solution/inventory/tsconfig.json @@ -52,6 +52,9 @@ "@kbn/rule-data-utils", "@kbn/spaces-plugin", "@kbn/cloud-plugin", - "@kbn/storybook" + "@kbn/storybook", + "@kbn/zod", + "@kbn/dashboard-plugin", + "@kbn/deeplinks-analytics" ] } diff --git a/x-pack/plugins/observability_solution/investigate_app/server/lib/get_sample_documents.ts b/x-pack/plugins/observability_solution/investigate_app/server/lib/get_sample_documents.ts index 75e21526a6506..9621a5ff1a4ed 100644 --- a/x-pack/plugins/observability_solution/investigate_app/server/lib/get_sample_documents.ts +++ b/x-pack/plugins/observability_solution/investigate_app/server/lib/get_sample_documents.ts @@ -7,7 +7,7 @@ import pLimit from 'p-limit'; import { estypes } from '@elastic/elasticsearch'; import { castArray, sortBy, uniq, partition, shuffle } from 'lodash'; -import { truncateList } from '@kbn/inference-plugin/common/util/truncate_list'; +import { truncateList } from '@kbn/inference-plugin/common/utils/truncate_list'; import { QueryDslQueryContainer } from '@kbn/data-views-plugin/common/types'; import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { rangeQuery, excludeFrozenQuery } from './queries'; diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/index.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/index.ts index faa848192fd46..44fe7d0cb1a8c 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/index.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/index.ts @@ -10,16 +10,6 @@ import { cpu } from './snapshot/cpu'; import { memory } from './snapshot/memory'; import { rx } from './snapshot/rx'; import { tx } from './snapshot/tx'; -import { containerCpuKernel } from './tsvb/container_cpu_kernel'; -import { containerCpuUsage } from './tsvb/container_cpu_usage'; -import { containerDiskIOOps } from './tsvb/container_diskio_ops'; -import { containerDiskIOBytes } from './tsvb/container_disk_io_bytes'; -import { containerK8sCpuUsage } from './tsvb/container_k8s_cpu_usage'; -import { containerK8sMemoryUsage } from './tsvb/container_k8s_memory_usage'; -import { containerK8sOverview } from './tsvb/container_k8s_overview'; -import { containerMemory } from './tsvb/container_memory'; -import { containerNetworkTraffic } from './tsvb/container_network_traffic'; -import { containerOverview } from './tsvb/container_overview'; import type { ContainerFormulas } from './formulas'; import { ContainerCharts } from './charts'; @@ -30,18 +20,6 @@ export const containerSnapshotMetricTypes = Object.keys(containerSnapshotMetrics >; export const metrics: InventoryMetricsWithCharts = { - tsvb: { - containerOverview, - containerCpuUsage, - containerCpuKernel, - containerDiskIOOps, - containerDiskIOBytes, - containerNetworkTraffic, - containerMemory, - containerK8sCpuUsage, - containerK8sOverview, - containerK8sMemoryUsage, - }, snapshot: containerSnapshotMetrics, getFormulas: async () => await import('./formulas').then(({ formulas }) => formulas), getCharts: async () => await import('./charts').then(({ charts }) => charts), diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_cpu_kernel.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_cpu_kernel.ts deleted file mode 100644 index f469a9e86ad49..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_cpu_kernel.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const containerCpuKernel: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'containerCpuKernel', - requires: ['docker.cpu'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'kernel', - split_mode: 'everything', - metrics: [ - { - field: 'docker.cpu.kernel.pct', - id: 'avg-cpu-kernel', - type: 'avg', - }, - ], - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_cpu_usage.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_cpu_usage.ts deleted file mode 100644 index f4efc7de43663..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_cpu_usage.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const containerCpuUsage: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'containerCpuUsage', - requires: ['docker.cpu'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'cpu', - split_mode: 'everything', - metrics: [ - { - field: 'docker.cpu.total.pct', - id: 'avg-cpu-total', - type: 'avg', - }, - ], - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_disk_io_bytes.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_disk_io_bytes.ts deleted file mode 100644 index 7320349f92e8d..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_disk_io_bytes.ts +++ /dev/null @@ -1,81 +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 { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const containerDiskIOBytes: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'containerDiskIOBytes', - requires: ['docker.diskio'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'read', - split_mode: 'everything', - metrics: [ - { - field: 'docker.diskio.read.bytes', - id: 'max-diskio-read-bytes', - type: 'max', - }, - { - field: 'max-diskio-read-bytes', - id: 'deriv-max-diskio-read-bytes', - type: 'derivative', - unit: '1s', - }, - { - id: 'posonly-deriv-max-diskio-read-bytes', - type: 'calculation', - variables: [{ id: 'var-rate', name: 'rate', field: 'deriv-max-diskio-read-bytes' }], - script: 'params.rate > 0.0 ? params.rate : 0.0', - }, - ], - }, - { - id: 'write', - split_mode: 'everything', - metrics: [ - { - field: 'docker.diskio.write.bytes', - id: 'max-diskio-write-bytes', - type: 'max', - }, - { - field: 'max-diskio-write-bytes', - id: 'deriv-max-diskio-write-bytes', - type: 'derivative', - unit: '1s', - }, - { - id: 'posonly-deriv-max-diskio-write-bytes', - type: 'calculation', - variables: [{ id: 'var-rate', name: 'rate', field: 'deriv-max-diskio-write-bytes' }], - script: 'params.rate > 0.0 ? params.rate : 0.0', - }, - { - id: 'calc-invert-rate', - script: 'params.rate * -1', - type: 'calculation', - variables: [ - { - field: 'posonly-deriv-max-diskio-write-bytes', - id: 'var-rate', - name: 'rate', - }, - ], - }, - ], - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_diskio_ops.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_diskio_ops.ts deleted file mode 100644 index a6887fb4e7638..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_diskio_ops.ts +++ /dev/null @@ -1,81 +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 { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const containerDiskIOOps: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'containerDiskIOOps', - requires: ['docker.diskio'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'read', - split_mode: 'everything', - metrics: [ - { - field: 'docker.diskio.read.ops', - id: 'max-diskio-read-ops', - type: 'max', - }, - { - field: 'max-diskio-read-ops', - id: 'deriv-max-diskio-read-ops', - type: 'derivative', - unit: '1s', - }, - { - id: 'posonly-deriv-max-diskio-read-ops', - type: 'calculation', - variables: [{ id: 'var-rate', name: 'rate', field: 'deriv-max-diskio-read-ops' }], - script: 'params.rate > 0.0 ? params.rate : 0.0', - }, - ], - }, - { - id: 'write', - split_mode: 'everything', - metrics: [ - { - field: 'docker.diskio.write.ops', - id: 'max-diskio-write-ops', - type: 'max', - }, - { - field: 'max-diskio-write-ops', - id: 'deriv-max-diskio-write-ops', - type: 'derivative', - unit: '1s', - }, - { - id: 'posonly-deriv-max-diskio-write-ops', - type: 'calculation', - variables: [{ id: 'var-rate', name: 'rate', field: 'deriv-max-diskio-write-ops' }], - script: 'params.rate > 0.0 ? params.rate : 0.0', - }, - { - id: 'calc-invert-rate', - script: 'params.rate * -1', - type: 'calculation', - variables: [ - { - field: 'posonly-deriv-max-diskio-write-ops', - id: 'var-rate', - name: 'rate', - }, - ], - }, - ], - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_k8s_cpu_usage.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_k8s_cpu_usage.ts deleted file mode 100644 index 67372f50d750b..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_k8s_cpu_usage.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const containerK8sCpuUsage: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'containerK8sCpuUsage', - requires: ['kubernetes.container'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'cpu', - split_mode: 'everything', - metrics: [ - { - field: 'kubernetes.container.cpu.usage.limit.pct', - id: 'avg-cpu', - type: 'avg', - }, - ], - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_k8s_memory_usage.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_k8s_memory_usage.ts deleted file mode 100644 index 066d993817b75..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_k8s_memory_usage.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const containerK8sMemoryUsage: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'containerK8sMemoryUsage', - requires: ['kubernetes.container'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'memory', - split_mode: 'everything', - metrics: [ - { - field: 'kubernetes.container.memory.usage.limit.pct', - id: 'avg-memory', - type: 'avg', - }, - ], - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_k8s_overview.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_k8s_overview.ts deleted file mode 100644 index 470704140efb4..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_k8s_overview.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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const containerK8sOverview: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'containerK8sOverview', - requires: ['kubernetes.container'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'cpu', - split_mode: 'everything', - metrics: [ - { - field: 'kubernetes.container.cpu.usage.limit.pct', - id: 'avg-cpu-total', - type: 'avg', - }, - ], - }, - { - id: 'memory', - split_mode: 'everything', - metrics: [ - { - field: 'kubernetes.container.memory.usage.limit.pct', - id: 'avg-memory-total', - type: 'avg', - }, - ], - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_memory.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_memory.ts deleted file mode 100644 index e0572692d60fc..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_memory.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const containerMemory: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'containerMemory', - requires: ['docker.memory'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'memory', - split_mode: 'everything', - metrics: [ - { - field: 'docker.memory.usage.pct', - id: 'avg-memory', - type: 'avg', - }, - ], - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_network_traffic.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_network_traffic.ts deleted file mode 100644 index d957d51a41648..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_network_traffic.ts +++ /dev/null @@ -1,93 +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 { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const containerNetworkTraffic: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'containerNetworkTraffic', - requires: ['docker.network'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'tx', - metrics: [ - { - field: 'docker.network.outbound.bytes', - id: 'max-net-out', - type: 'max', - }, - { - field: 'max-net-out', - id: 'deriv-max-net-out', - type: 'derivative', - unit: '1s', - }, - { - id: 'posonly-deriv-max-net-out', - type: 'calculation', - variables: [{ id: 'var-rate', name: 'rate', field: 'deriv-max-net-out' }], - script: 'params.rate > 0.0 ? params.rate : 0.0', - }, - { - function: 'sum', - id: 'seriesagg-sum', - type: 'series_agg', - }, - ], - split_mode: 'terms', - terms_field: 'docker.network.interface', - }, - { - id: 'rx', - metrics: [ - { - field: 'docker.network.inbound.bytes', - id: 'max-net-in', - type: 'max', - }, - { - field: 'max-net-in', - id: 'deriv-max-net-in', - type: 'derivative', - unit: '1s', - }, - { - id: 'posonly-deriv-max-net-in', - type: 'calculation', - variables: [{ id: 'var-rate', name: 'rate', field: 'deriv-max-net-in' }], - script: 'params.rate > 0.0 ? params.rate : 0.0', - }, - { - id: 'calc-invert-rate', - script: 'params.rate * -1', - type: 'calculation', - variables: [ - { - field: 'posonly-deriv-max-net-in', - id: 'var-rate', - name: 'rate', - }, - ], - }, - { - function: 'sum', - id: 'seriesagg-sum', - type: 'series_agg', - }, - ], - split_mode: 'terms', - terms_field: 'docker.network.interface', - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_overview.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_overview.ts deleted file mode 100644 index c2d234be2eed7..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_overview.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 { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const containerOverview: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'containerOverview', - requires: ['docker'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'cpu', - split_mode: 'everything', - metrics: [ - { - field: 'docker.cpu.total.pct', - id: 'avg-cpu-total', - type: 'avg', - }, - ], - }, - { - id: 'memory', - split_mode: 'everything', - metrics: [ - { - field: 'docker.memory.usage.pct', - id: 'avg-memory', - type: 'avg', - }, - ], - }, - { - id: 'tx', - split_mode: 'everything', - metrics: [ - { - field: 'docker.network.out.bytes', - id: 'avg-network-out', - type: 'avg', - }, - ], - }, - { - id: 'rx', - split_mode: 'everything', - metrics: [ - { - field: 'docker.network.in.bytes', - id: 'avg-network-in', - type: 'avg', - }, - ], - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/index.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/index.ts index ce56e7bd59b0b..82408084e9472 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/index.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/index.ts @@ -5,7 +5,6 @@ * 2.0. */ import { snapshot } from './snapshot'; -import { tsvb } from './tsvb'; import { InventoryMetricsWithCharts } from '../../types'; import type { HostFormulas } from './formulas'; import type { HostCharts } from './charts'; @@ -18,7 +17,6 @@ export const hostSnapshotMetricTypes = Object.keys(exposedHostSnapshotMetrics) a >; export const metrics: InventoryMetricsWithCharts = { - tsvb, snapshot, getFormulas: async () => await import('./formulas').then(({ formulas }) => formulas), getCharts: async () => await import('./charts').then(({ charts }) => charts), diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_cpu_usage.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_cpu_usage.ts deleted file mode 100644 index bcafeb4ebc4cf..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_cpu_usage.ts +++ /dev/null @@ -1,254 +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 { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const hostCpuUsage: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'hostCpuUsage', - requires: ['system.cpu'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'user', - metrics: [ - { - field: 'system.cpu.user.pct', - id: 'avg-cpu-user', - type: 'avg', - }, - { - field: 'system.cpu.cores', - id: 'max-cpu-cores', - type: 'max', - }, - { - id: 'calc-avg-cores', - script: 'params.avg / params.cores', - type: 'calculation', - variables: [ - { - field: 'max-cpu-cores', - id: 'var-cores', - name: 'cores', - }, - { - field: 'avg-cpu-user', - id: 'var-avg', - name: 'avg', - }, - ], - }, - ], - split_mode: 'everything', - }, - { - id: 'system', - metrics: [ - { - field: 'system.cpu.system.pct', - id: 'avg-cpu-system', - type: 'avg', - }, - { - field: 'system.cpu.cores', - id: 'max-cpu-cores', - type: 'max', - }, - { - id: 'calc-avg-cores', - script: 'params.avg / params.cores', - type: 'calculation', - variables: [ - { - field: 'max-cpu-cores', - id: 'var-cores', - name: 'cores', - }, - { - field: 'avg-cpu-system', - id: 'var-avg', - name: 'avg', - }, - ], - }, - ], - split_mode: 'everything', - }, - { - id: 'steal', - metrics: [ - { - field: 'system.cpu.steal.pct', - id: 'avg-cpu-steal', - type: 'avg', - }, - { - field: 'system.cpu.cores', - id: 'max-cpu-cores', - type: 'max', - }, - { - id: 'calc-avg-cores', - script: 'params.avg / params.cores', - type: 'calculation', - variables: [ - { - field: 'avg-cpu-steal', - id: 'var-avg', - name: 'avg', - }, - { - field: 'max-cpu-cores', - id: 'var-cores', - name: 'cores', - }, - ], - }, - ], - split_mode: 'everything', - }, - { - id: 'irq', - metrics: [ - { - field: 'system.cpu.irq.pct', - id: 'avg-cpu-irq', - type: 'avg', - }, - { - field: 'system.cpu.cores', - id: 'max-cpu-cores', - type: 'max', - }, - { - id: 'calc-avg-cores', - script: 'params.avg / params.cores', - type: 'calculation', - variables: [ - { - field: 'max-cpu-cores', - id: 'var-cores', - name: 'cores', - }, - { - field: 'avg-cpu-irq', - id: 'var-avg', - name: 'avg', - }, - ], - }, - ], - split_mode: 'everything', - }, - { - id: 'softirq', - metrics: [ - { - field: 'system.cpu.softirq.pct', - id: 'avg-cpu-softirq', - type: 'avg', - }, - { - field: 'system.cpu.cores', - id: 'max-cpu-cores', - type: 'max', - }, - { - id: 'calc-avg-cores', - script: 'params.avg / params.cores', - type: 'calculation', - variables: [ - { - field: 'max-cpu-cores', - id: 'var-cores', - name: 'cores', - }, - { - field: 'avg-cpu-softirq', - id: 'var-avg', - name: 'avg', - }, - ], - }, - ], - split_mode: 'everything', - }, - { - id: 'iowait', - metrics: [ - { - field: 'system.cpu.iowait.pct', - id: 'avg-cpu-iowait', - type: 'avg', - }, - { - field: 'system.cpu.cores', - id: 'max-cpu-cores', - type: 'max', - }, - { - id: 'calc-avg-cores', - script: 'params.avg / params.cores', - type: 'calculation', - variables: [ - { - field: 'max-cpu-cores', - id: 'var-cores', - name: 'cores', - }, - { - field: 'avg-cpu-iowait', - id: 'var-avg', - name: 'avg', - }, - ], - }, - ], - split_mode: 'everything', - }, - { - id: 'nice', - metrics: [ - { - field: 'system.cpu.nice.pct', - id: 'avg-cpu-nice', - type: 'avg', - }, - { - field: 'system.cpu.cores', - id: 'max-cpu-cores', - type: 'max', - }, - { - id: 'calc-avg-cores', - script: 'params.avg / params.cores', - type: 'calculation', - variables: [ - { - field: 'max-cpu-cores', - id: 'var-cores', - name: 'cores', - }, - { - field: 'avg-cpu-nice', - id: 'var-avg', - name: 'avg', - }, - ], - }, - ], - split_mode: 'everything', - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_docker_info.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_docker_info.ts deleted file mode 100644 index 4cc2b574362d7..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_docker_info.ts +++ /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 { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const hostDockerInfo: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'hostDockerInfo', - requires: ['docker.info'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'running', - metrics: [ - { - field: 'docker.info.containers.running', - id: 'max-running', - type: 'max', - }, - ], - split_mode: 'everything', - }, - { - id: 'paused', - metrics: [ - { - field: 'docker.info.containers.paused', - id: 'max-paused', - type: 'max', - }, - ], - split_mode: 'everything', - }, - { - id: 'stopped', - metrics: [ - { - field: 'docker.info.containers.stopped', - id: 'max-stopped', - type: 'max', - }, - ], - split_mode: 'everything', - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_docker_overview.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_docker_overview.ts deleted file mode 100644 index df56a21dbf5b7..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_docker_overview.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 { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const hostDockerOverview: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'hostDockerOverview', - requires: ['docker.info'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'top_n', - series: [ - { - id: 'total', - metrics: [ - { - field: 'docker.info.containers.total', - id: 'max-total', - type: 'max', - }, - ], - split_mode: 'everything', - }, - { - id: 'running', - metrics: [ - { - field: 'docker.info.containers.running', - id: 'max-running', - type: 'max', - }, - ], - split_mode: 'everything', - }, - { - id: 'paused', - metrics: [ - { - field: 'docker.info.containers.paused', - id: 'max-paused', - type: 'max', - }, - ], - split_mode: 'everything', - }, - { - id: 'stopped', - metrics: [ - { - field: 'docker.info.containers.stopped', - id: 'max-stopped', - type: 'max', - }, - ], - split_mode: 'everything', - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_docker_top_5_by_cpu.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_docker_top_5_by_cpu.ts deleted file mode 100644 index fd9eb97419736..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_docker_top_5_by_cpu.ts +++ /dev/null @@ -1,37 +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 { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const hostDockerTop5ByCpu: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'hostDockerTop5ByCpu', - requires: ['docker.cpu'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'avg-cpu', - metrics: [ - { - field: 'docker.cpu.total.pct', - id: 'avg-cpu-metric', - type: 'avg', - }, - ], - split_mode: 'terms', - terms_field: 'container.name', - terms_order_by: 'avg-cpu', - terms_size: 5, - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_docker_top_5_by_memory.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_docker_top_5_by_memory.ts deleted file mode 100644 index cad828671d232..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_docker_top_5_by_memory.ts +++ /dev/null @@ -1,37 +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 { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const hostDockerTop5ByMemory: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'hostDockerTop5ByMemory', - requires: ['docker.memory'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'avg-memory', - metrics: [ - { - field: 'docker.memory.usage.pct', - id: 'avg-memory-metric', - type: 'avg', - }, - ], - split_mode: 'terms', - terms_field: 'container.name', - terms_order_by: 'avg-memory', - terms_size: 5, - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_filesystem.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_filesystem.ts deleted file mode 100644 index ce284345410fc..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_filesystem.ts +++ /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 { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const hostFilesystem: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'hostFilesystem', - requires: ['system.filesystem'], - filter: 'system.filesystem.device_name:\\/*', - index_pattern: indexPattern, - time_field: timeField, - interval, - type: 'timeseries', - series: [ - { - id: 'used', - metrics: [ - { - field: 'system.filesystem.used.pct', - id: 'avg-filesystem-used', - type: 'avg', - }, - ], - split_mode: 'terms', - terms_field: 'system.filesystem.device_name', - terms_order_by: 'used', - terms_size: 5, - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_k8s_cpu_cap.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_k8s_cpu_cap.ts deleted file mode 100644 index d33e4cdeb34cd..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_k8s_cpu_cap.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const hostK8sCpuCap: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'hostK8sCpuCap', - map_field_to: 'kubernetes.node.name', - requires: ['kubernetes.node'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'capacity', - metrics: [ - { - field: 'kubernetes.node.cpu.allocatable.cores', - id: 'max-cpu-cap', - type: 'max', - }, - { - id: 'calc-nanocores', - type: 'calculation', - variables: [ - { - id: 'var-cores', - field: 'max-cpu-cap', - name: 'cores', - }, - ], - script: 'params.cores * 1000000000', - }, - ], - split_mode: 'everything', - }, - { - id: 'used', - metrics: [ - { - field: 'kubernetes.node.cpu.usage.nanocores', - id: 'avg-cpu-usage', - type: 'avg', - }, - ], - split_mode: 'everything', - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_k8s_disk_cap.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_k8s_disk_cap.ts deleted file mode 100644 index e9e512a136631..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_k8s_disk_cap.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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; -export const hostK8sDiskCap: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'hostK8sDiskCap', - map_field_to: 'kubernetes.node.name', - requires: ['kubernetes.node'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'capacity', - metrics: [ - { - field: 'kubernetes.node.fs.capacity.bytes', - id: 'max-fs-cap', - type: 'max', - }, - ], - split_mode: 'everything', - }, - { - id: 'used', - metrics: [ - { - field: 'kubernetes.node.fs.used.bytes', - id: 'avg-fs-used', - type: 'avg', - }, - ], - split_mode: 'everything', - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_k8s_memory_cap.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_k8s_memory_cap.ts deleted file mode 100644 index ccc227ef1854e..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_k8s_memory_cap.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 { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const hostK8sMemoryCap: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'hostK8sMemoryCap', - map_field_to: 'kubernetes.node.name', - requires: ['kubernetes.node'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'capacity', - metrics: [ - { - field: 'kubernetes.node.memory.allocatable.bytes', - id: 'max-memory-cap', - type: 'max', - }, - ], - split_mode: 'everything', - }, - { - id: 'used', - metrics: [ - { - field: 'kubernetes.node.memory.usage.bytes', - id: 'avg-memory-usage', - type: 'avg', - }, - ], - split_mode: 'everything', - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_k8s_overview.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_k8s_overview.ts deleted file mode 100644 index 2da74d50dff1b..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_k8s_overview.ts +++ /dev/null @@ -1,155 +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 { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const hostK8sOverview: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'hostK8sOverview', - requires: ['kubernetes'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'top_n', - series: [ - { - id: 'cpucap', - split_mode: 'everything', - metrics: [ - { - field: 'kubernetes.node.cpu.allocatable.cores', - id: 'max-cpu-cap', - type: 'max', - }, - { - field: 'kubernetes.node.cpu.usage.nanocores', - id: 'avg-cpu-usage', - type: 'avg', - }, - { - id: 'calc-used-cap', - script: 'params.used / (params.cap * 1000000000)', - type: 'calculation', - variables: [ - { - field: 'max-cpu-cap', - id: 'var-cap', - name: 'cap', - }, - { - field: 'avg-cpu-usage', - id: 'var-used', - name: 'used', - }, - ], - }, - ], - }, - { - id: 'diskcap', - metrics: [ - { - field: 'kubernetes.node.fs.capacity.bytes', - id: 'max-fs-cap', - type: 'max', - }, - { - field: 'kubernetes.node.fs.used.bytes', - id: 'avg-fs-used', - type: 'avg', - }, - { - id: 'calc-used-cap', - script: 'params.used / params.cap', - type: 'calculation', - variables: [ - { - field: 'max-fs-cap', - id: 'var-cap', - name: 'cap', - }, - { - field: 'avg-fs-used', - id: 'var-used', - name: 'used', - }, - ], - }, - ], - split_mode: 'everything', - }, - { - id: 'memorycap', - metrics: [ - { - field: 'kubernetes.node.memory.allocatable.bytes', - id: 'max-memory-cap', - type: 'max', - }, - { - field: 'kubernetes.node.memory.usage.bytes', - id: 'avg-memory-usage', - type: 'avg', - }, - { - id: 'calc-used-cap', - script: 'params.used / params.cap', - type: 'calculation', - variables: [ - { - field: 'max-memory-cap', - id: 'var-cap', - name: 'cap', - }, - { - field: 'avg-memory-usage', - id: 'var-used', - name: 'used', - }, - ], - }, - ], - split_mode: 'everything', - }, - { - id: 'podcap', - metrics: [ - { - field: 'kubernetes.node.pod.capacity.total', - id: 'max-pod-cap', - type: 'max', - }, - { - field: 'kubernetes.pod.uid', - id: 'card-pod-name', - type: 'cardinality', - }, - { - id: 'calc-used-cap', - script: 'params.used / params.cap', - type: 'calculation', - variables: [ - { - field: 'max-pod-cap', - id: 'var-cap', - name: 'cap', - }, - { - field: 'card-pod-name', - id: 'var-used', - name: 'used', - }, - ], - }, - ], - split_mode: 'everything', - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_k8s_pod_cap.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_k8s_pod_cap.ts deleted file mode 100644 index 85cb798eaf9b9..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_k8s_pod_cap.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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const hostK8sPodCap: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'hostK8sPodCap', - requires: ['kubernetes.node'], - map_field_to: 'kubernetes.node.name', - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - - series: [ - { - id: 'capacity', - metrics: [ - { - field: 'kubernetes.node.pod.allocatable.total', - id: 'max-pod-cap', - type: 'max', - }, - ], - split_mode: 'everything', - }, - { - id: 'used', - metrics: [ - { - field: 'kubernetes.pod.uid', - id: 'avg-pod', - type: 'cardinality', - }, - ], - split_mode: 'everything', - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_load.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_load.ts deleted file mode 100644 index bef170c743e6c..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_load.ts +++ /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 { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const hostLoad: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'hostLoad', - requires: ['system.cpu'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'load_1m', - metrics: [ - { - field: 'system.load.1', - id: 'avg-load-1m', - type: 'avg', - }, - ], - split_mode: 'everything', - }, - { - id: 'load_5m', - metrics: [ - { - field: 'system.load.5', - id: 'avg-load-5m', - type: 'avg', - }, - ], - split_mode: 'everything', - }, - { - id: 'load_15m', - metrics: [ - { - field: 'system.load.15', - id: 'avg-load-15m', - type: 'avg', - }, - ], - split_mode: 'everything', - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_memory_usage.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_memory_usage.ts deleted file mode 100644 index bda81dea4bd28..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_memory_usage.ts +++ /dev/null @@ -1,78 +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 { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const hostMemoryUsage: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'hostMemoryUsage', - requires: ['system.memory'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'free', - metrics: [ - { - field: 'system.memory.free', - id: 'avg-memory-free', - type: 'avg', - }, - ], - split_mode: 'everything', - }, - { - id: 'used', - metrics: [ - { - field: 'system.memory.actual.used.bytes', - id: 'avg-memory-used', - type: 'avg', - }, - ], - split_mode: 'everything', - }, - { - id: 'cache', - metrics: [ - { - field: 'system.memory.actual.used.bytes', - id: 'avg-memory-actual-used', - type: 'avg', - }, - { - field: 'system.memory.used.bytes', - id: 'avg-memory-used', - type: 'avg', - }, - { - id: 'calc-used-actual', - script: 'params.used - params.actual', - type: 'calculation', - variables: [ - { - field: 'avg-memory-actual-used', - id: 'var-actual', - name: 'actual', - }, - { - field: 'avg-memory-used', - id: 'var-used', - name: 'used', - }, - ], - }, - ], - split_mode: 'everything', - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_network_traffic.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_network_traffic.ts deleted file mode 100644 index b3dfc5e91ba0a..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_network_traffic.ts +++ /dev/null @@ -1,109 +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 { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const hostNetworkTraffic: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'hostNetworkTraffic', - requires: ['system.network'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'timeseries', - series: [ - { - id: 'tx', - metrics: [ - { - field: 'host.network.egress.bytes', - id: 'avg-net-out', - type: 'avg', - }, - { - id: 'max-period', - type: 'max', - field: 'metricset.period', - }, - { - id: '3216b170-f192-11ec-a8e3-dd984b7213e2', - type: 'calculation', - variables: [ - { - id: '34e64c30-f192-11ec-a8e3-dd984b7213e2', - name: 'value', - field: 'avg-net-out', - }, - { - id: '3886cb80-f192-11ec-a8e3-dd984b7213e2', - name: 'period', - field: 'max-period', - }, - ], - script: 'params.value / (params.period / 1000)', - }, - ], - filter: { - language: 'kuery', - query: 'host.network.egress.bytes : * ', - }, - split_mode: 'everything', - }, - { - id: 'rx', - metrics: [ - { - field: 'host.network.ingress.bytes', - id: 'avg-net-in', - type: 'avg', - }, - { - id: 'calc-invert-rate', - script: 'params.rate * -1', - type: 'calculation', - variables: [ - { - field: 'avg-net-in', - id: 'var-rate', - name: 'rate', - }, - ], - }, - { - id: 'max-period', - type: 'max', - field: 'metricset.period', - }, - { - id: '3216b170-f192-11ec-a8e3-dd984b7213e2', - type: 'calculation', - variables: [ - { - id: '34e64c30-f192-11ec-a8e3-dd984b7213e2', - name: 'value', - field: 'calc-invert-rate', - }, - { - id: '3886cb80-f192-11ec-a8e3-dd984b7213e2', - name: 'period', - field: 'max-period', - }, - ], - script: 'params.value / (params.period / 1000)', - }, - ], - filter: { - language: 'kuery', - query: 'host.network.ingress.bytes : * ', - }, - split_mode: 'everything', - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_system_overview.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_system_overview.ts deleted file mode 100644 index 69ebd0aa35947..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/host_system_overview.ts +++ /dev/null @@ -1,130 +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 { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; - -export const hostSystemOverview: TSVBMetricModelCreator = ( - timeField, - indexPattern, - interval -): TSVBMetricModel => ({ - id: 'hostSystemOverview', - requires: ['system.cpu', 'system.memory', 'system.load', 'system.network'], - index_pattern: indexPattern, - interval, - time_field: timeField, - type: 'top_n', - series: [ - { - id: 'cpu', - split_mode: 'everything', - metrics: [ - { - field: 'system.cpu.total.norm.pct', - id: 'avg-cpu-total', - type: 'avg', - }, - ], - }, - { - id: 'load', - split_mode: 'everything', - metrics: [ - { - field: 'system.load.5', - id: 'avg-load-5m', - type: 'avg', - }, - ], - }, - { - id: 'memory', - split_mode: 'everything', - metrics: [ - { - field: 'system.memory.actual.used.pct', - id: 'avg-memory-actual-used', - type: 'avg', - }, - ], - }, - { - id: 'rx', - metrics: [ - { - field: 'host.network.ingress.bytes', - id: 'avg-net-in', - type: 'avg', - }, - { - id: 'max-period', - type: 'max', - field: 'metricset.period', - }, - { - id: '3216b170-f192-11ec-a8e3-dd984b7213e2', - type: 'calculation', - variables: [ - { - id: '34e64c30-f192-11ec-a8e3-dd984b7213e2', - name: 'value', - field: 'avg-net-in', - }, - { - id: '3886cb80-f192-11ec-a8e3-dd984b7213e2', - name: 'period', - field: 'max-period', - }, - ], - script: 'params.value / (params.period / 1000)', - }, - ], - filter: { - language: 'kuery', - query: 'host.network.ingress.bytes : * ', - }, - split_mode: 'everything', - }, - { - id: 'tx', - metrics: [ - { - field: 'host.network.egress.bytes', - id: 'avg-net-out', - type: 'avg', - }, - { - id: 'max-period', - type: 'max', - field: 'metricset.period', - }, - { - id: '3216b170-f192-11ec-a8e3-dd984b7213e2', - type: 'calculation', - variables: [ - { - id: '34e64c30-f192-11ec-a8e3-dd984b7213e2', - name: 'value', - field: 'avg-net-out', - }, - { - id: '3886cb80-f192-11ec-a8e3-dd984b7213e2', - name: 'period', - field: 'max-period', - }, - ], - script: 'params.value / (params.period / 1000)', - }, - ], - filter: { - language: 'kuery', - query: 'host.network.egress.bytes : * ', - }, - split_mode: 'everything', - }, - ], -}); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/index.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/index.ts deleted file mode 100644 index fb91ee9dd8b27..0000000000000 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/tsvb/index.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { 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/observability_solution/metrics_data_access/common/inventory_models/metrics.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/metrics.ts index 00937a064a7b8..510e655c0b8d7 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/metrics.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/metrics.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { metrics as hostMetrics } from './host/metrics'; import { metrics as sharedMetrics } from './shared/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'; import { metrics as awsRDSMetrics } from './aws_rds/metrics'; @@ -16,10 +14,8 @@ import { metrics as awsSQSMetrics } from './aws_sqs/metrics'; export const metrics = { tsvb: { - ...hostMetrics.tsvb, ...sharedMetrics.tsvb, ...podMetrics.tsvb, - ...containerMetrics.tsvb, ...awsEC2Metrics.tsvb, ...awsS3Metrics.tsvb, ...awsRDSMetrics.tsvb, diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/types.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/types.ts index f4054f289ae7a..8ad0ff886ebe6 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/types.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/types.ts @@ -46,7 +46,6 @@ export const InventoryMetricRT = rt.keyof({ hostSystemOverview: null, hostCpuUsageTotal: null, hostCpuUsage: null, - hostFilesystem: null, hostK8sOverview: null, hostK8sCpuCap: null, hostK8sDiskCap: null, @@ -55,17 +54,12 @@ export const InventoryMetricRT = rt.keyof({ hostLoad: null, hostMemoryUsage: null, hostNetworkTraffic: null, - hostDockerOverview: null, - hostDockerInfo: null, - hostDockerTop5ByCpu: null, - hostDockerTop5ByMemory: null, podOverview: null, podCpuUsage: null, podMemoryUsage: null, podLogUsage: null, podNetworkTraffic: null, containerOverview: null, - containerCpuKernel: null, containerCpuUsage: null, containerDiskIOOps: null, containerDiskIOBytes: null, @@ -276,7 +270,7 @@ export const SnapshotMetricTypeRT = rt.keyof(SnapshotMetricTypeKeys); export type SnapshotMetricType = rt.TypeOf; export interface InventoryMetrics { - tsvb: { [name: string]: TSVBMetricModelCreator }; + tsvb?: { [name: string]: TSVBMetricModelCreator }; snapshot: { [name: string]: MetricsUIAggregation | undefined }; defaultSnapshot: SnapshotMetricType; /** This is used by the inventory view to calculate the appropriate amount of time for the metrics detail page. Some metrics like awsS3 require multiple days where others like host only need an hour.*/ diff --git a/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.ts b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.ts index b5819631406d7..e9e8714bf85b9 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.ts @@ -286,7 +286,7 @@ export const mapRuleParamsWithFlyout = (alert: TopAlert): FlyoutThresholdData[] const { thresholdComparator, threshold } = ruleParams as EsQueryRuleParams; const ESQueryFlyoutMap = { observedValue: [alert.fields[ALERT_EVALUATION_VALUE]], - threshold: threshold.join(' AND '), + threshold: [threshold].flat().join(' AND '), comparator: thresholdComparator, pctAboveThreshold: getPctAboveThreshold( threshold, diff --git a/x-pack/plugins/observability_solution/observability/public/pages/alert_details/alert_details.tsx b/x-pack/plugins/observability_solution/observability/public/pages/alert_details/alert_details.tsx index 8f5acee54f57e..9403090b1e213 100644 --- a/x-pack/plugins/observability_solution/observability/public/pages/alert_details/alert_details.tsx +++ b/x-pack/plugins/observability_solution/observability/public/pages/alert_details/alert_details.tsx @@ -93,6 +93,7 @@ export function AlertDetails() { triggersActionsUi: { ruleTypeRegistry }, observabilityAIAssistant, uiSettings, + serverless, } = useKibana().services; const { search } = useLocation(); @@ -158,20 +159,23 @@ export function AlertDetails() { } }, [alertDetail, ruleTypeRegistry]); - useBreadcrumbs([ - { - href: http.basePath.prepend(paths.observability.alerts), - text: i18n.translate('xpack.observability.breadcrumbs.alertsLinkText', { - defaultMessage: 'Alerts', - }), - deepLinkId: 'observability-overview:alerts', - }, - { - text: alertDetail - ? getPageTitle(alertDetail.formatted.fields[ALERT_RULE_CATEGORY]) - : defaultBreadcrumb, - }, - ]); + useBreadcrumbs( + [ + { + href: http.basePath.prepend(paths.observability.alerts), + text: i18n.translate('xpack.observability.breadcrumbs.alertsLinkText', { + defaultMessage: 'Alerts', + }), + deepLinkId: 'observability-overview:alerts', + }, + { + text: alertDetail + ? getPageTitle(alertDetail.formatted.fields[ALERT_RULE_CATEGORY]) + : defaultBreadcrumb, + }, + ], + { serverless } + ); const onUntrackAlert = () => { setAlertStatus(ALERT_STATUS_UNTRACKED); diff --git a/x-pack/plugins/observability_solution/observability/public/pages/alerts/alerts.tsx b/x-pack/plugins/observability_solution/observability/public/pages/alerts/alerts.tsx index ef883f40f4902..6607052225555 100644 --- a/x-pack/plugins/observability_solution/observability/public/pages/alerts/alerts.tsx +++ b/x-pack/plugins/observability_solution/observability/public/pages/alerts/alerts.tsx @@ -159,13 +159,18 @@ function InternalAlertsPage() { [alertSearchBarStateProps.rangeFrom, alertSearchBarStateProps.rangeTo, bucketSize, esQuery] ); - useBreadcrumbs([ + useBreadcrumbs( + [ + { + text: i18n.translate('xpack.observability.breadcrumbs.alertsLinkText', { + defaultMessage: 'Alerts', + }), + }, + ], { - text: i18n.translate('xpack.observability.breadcrumbs.alertsLinkText', { - defaultMessage: 'Alerts', - }), - }, - ]); + classicOnly: true, + } + ); async function loadRuleStats() { setRuleStatsLoading(true); diff --git a/x-pack/plugins/observability_solution/observability/public/pages/overview/overview.tsx b/x-pack/plugins/observability_solution/observability/public/pages/overview/overview.tsx index 37942cdbca7d6..c7b71431050cf 100644 --- a/x-pack/plugins/observability_solution/observability/public/pages/overview/overview.tsx +++ b/x-pack/plugins/observability_solution/observability/public/pages/overview/overview.tsx @@ -56,13 +56,18 @@ export function OverviewPage() { const { ObservabilityPageTemplate, observabilityRuleTypeRegistry } = usePluginContext(); - useBreadcrumbs([ + useBreadcrumbs( + [ + { + text: i18n.translate('xpack.observability.breadcrumbs.overviewLinkText', { + defaultMessage: 'Overview', + }), + }, + ], { - text: i18n.translate('xpack.observability.breadcrumbs.overviewLinkText', { - defaultMessage: 'Overview', - }), - }, - ]); + classicOnly: true, + } + ); const { data: newsFeed } = useFetcher( () => getNewsFeed({ http, kibanaVersion }), diff --git a/x-pack/plugins/observability_solution/observability/public/pages/rule_details/rule_details.tsx b/x-pack/plugins/observability_solution/observability/public/pages/rule_details/rule_details.tsx index e8270434c12b2..3b2e5d3118c4a 100644 --- a/x-pack/plugins/observability_solution/observability/public/pages/rule_details/rule_details.tsx +++ b/x-pack/plugins/observability_solution/observability/public/pages/rule_details/rule_details.tsx @@ -61,6 +61,7 @@ export function RuleDetailsPage() { getRuleDefinition: RuleDefinition, getRuleStatusPanel: RuleStatusPanel, }, + serverless, } = useKibana().services; const { ObservabilityPageTemplate } = usePluginContext(); @@ -72,24 +73,27 @@ export function RuleDetailsPage() { filterByRuleTypeIds: filteredRuleTypes, }); - useBreadcrumbs([ - { - text: i18n.translate('xpack.observability.breadcrumbs.alertsLinkText', { - defaultMessage: 'Alerts', - }), - href: basePath.prepend(paths.observability.alerts), - deepLinkId: 'observability-overview:alerts', - }, - { - href: basePath.prepend(paths.observability.rules), - text: i18n.translate('xpack.observability.breadcrumbs.rulesLinkText', { - defaultMessage: 'Rules', - }), - }, - { - text: rule && rule.name, - }, - ]); + useBreadcrumbs( + [ + { + text: i18n.translate('xpack.observability.breadcrumbs.alertsLinkText', { + defaultMessage: 'Alerts', + }), + href: basePath.prepend(paths.observability.alerts), + deepLinkId: 'observability-overview:alerts', + }, + { + href: basePath.prepend(paths.observability.rules), + text: i18n.translate('xpack.observability.breadcrumbs.rulesLinkText', { + defaultMessage: 'Rules', + }), + }, + { + text: rule && rule.name, + }, + ], + { serverless } + ); const [activeTabId, setActiveTabId] = useState(() => { const searchParams = new URLSearchParams(search); diff --git a/x-pack/plugins/observability_solution/observability/public/pages/rules/rules.tsx b/x-pack/plugins/observability_solution/observability/public/pages/rules/rules.tsx index fb257f9f95cde..b94b9a7e4218f 100644 --- a/x-pack/plugins/observability_solution/observability/public/pages/rules/rules.tsx +++ b/x-pack/plugins/observability_solution/observability/public/pages/rules/rules.tsx @@ -42,6 +42,7 @@ export function RulesPage({ activeTab = RULES_TAB_NAME }: RulesPageProps) { getAddRuleFlyout: AddRuleFlyout, getRulesSettingsLink: RulesSettingsLink, }, + serverless, } = useKibana().services; const { ObservabilityPageTemplate } = usePluginContext(); const history = useHistory(); @@ -50,20 +51,23 @@ export function RulesPage({ activeTab = RULES_TAB_NAME }: RulesPageProps) { const [addRuleFlyoutVisibility, setAddRuleFlyoutVisibility] = useState(false); const [stateRefresh, setRefresh] = useState(new Date()); - useBreadcrumbs([ - { - text: i18n.translate('xpack.observability.breadcrumbs.alertsLinkText', { - defaultMessage: 'Alerts', - }), - href: http.basePath.prepend('/app/observability/alerts'), - deepLinkId: 'observability-overview:alerts', - }, - { - text: i18n.translate('xpack.observability.breadcrumbs.rulesLinkText', { - defaultMessage: 'Rules', - }), - }, - ]); + useBreadcrumbs( + [ + { + text: i18n.translate('xpack.observability.breadcrumbs.alertsLinkText', { + defaultMessage: 'Alerts', + }), + href: http.basePath.prepend('/app/observability/alerts'), + deepLinkId: 'observability-overview:alerts', + }, + { + text: i18n.translate('xpack.observability.breadcrumbs.rulesLinkText', { + defaultMessage: 'Rules', + }), + }, + ], + { serverless } + ); const filteredRuleTypes = useGetFilteredRuleTypes(); const { diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/common/types.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/common/types.ts index 7595c42e4dc93..51ae37b39d90f 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/common/types.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/common/types.ts @@ -5,7 +5,7 @@ * 2.0. */ import { IconType } from '@elastic/eui'; -import type { ToolSchema } from '@kbn/inference-plugin/common'; +import type { ToolSchema } from '@kbn/inference-common'; import type { AssistantScope } from '@kbn/ai-assistant-common'; import type { ObservabilityAIAssistantChatService } from '../public'; import type { FunctionResponse } from './functions/types'; diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/analytics/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/public/analytics/index.ts index 86b8f14bde9e4..049c5a1e65fb0 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/analytics/index.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/analytics/index.ts @@ -6,6 +6,7 @@ */ import type { AnalyticsServiceSetup, AnalyticsServiceStart } from '@kbn/core-analytics-browser'; +import { AssistantScope } from '@kbn/ai-assistant-common'; import type { Message } from '../../common'; import { chatFeedbackEventSchema, ChatFeedback } from './schemas/chat_feedback'; import { insightFeedbackEventSchema, InsightFeedback } from './schemas/insight_feedback'; @@ -17,7 +18,10 @@ const schemas = [chatFeedbackEventSchema, insightFeedbackEventSchema, userSentPr export type TelemetryEventTypeWithPayload = | { type: ObservabilityAIAssistantTelemetryEventType.ChatFeedback; payload: ChatFeedback } | { type: ObservabilityAIAssistantTelemetryEventType.InsightFeedback; payload: InsightFeedback } - | { type: ObservabilityAIAssistantTelemetryEventType.UserSentPromptInChat; payload: Message }; + | { + type: ObservabilityAIAssistantTelemetryEventType.UserSentPromptInChat; + payload: Message & { scopes: AssistantScope[] }; + }; export const registerTelemetryEventTypes = (analytics: AnalyticsServiceSetup) => { schemas.forEach((schema) => { diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/analytics/schemas/common.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/public/analytics/schemas/common.ts index b01a8e05a4ea5..4a2739ef82c35 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/analytics/schemas/common.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/analytics/schemas/common.ts @@ -6,9 +6,10 @@ */ import type { RootSchema } from '@kbn/core/public'; +import { AssistantScope } from '@kbn/ai-assistant-common'; import type { Message } from '../../../common'; -export const messageSchema: RootSchema = { +export const messageSchema: RootSchema = { '@timestamp': { type: 'text', _meta: { @@ -74,4 +75,13 @@ export const messageSchema: RootSchema = { }, }, }, + scopes: { + type: 'array', + items: { + type: 'text', + _meta: { + description: 'The scopes that were used when generating the message.', + }, + }, + }, }; diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/components/insight/insight.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant/public/components/insight/insight.tsx index 562749f24cc9d..e5168470be8f1 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/components/insight/insight.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/components/insight/insight.tsx @@ -128,6 +128,7 @@ function ChatContent({ service.conversations.openNewConversation({ messages, title: defaultTitle, + hideConversationList: true, }); }} /> diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_service.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_service.ts index 07f967a4028d9..5b64073592430 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_service.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_service.ts @@ -36,7 +36,11 @@ export function createService({ const screenContexts$ = new BehaviorSubject([ { starterPrompts: defaultStarterPrompts }, ]); - const predefinedConversation$ = new Subject<{ messages: Message[]; title?: string }>(); + const predefinedConversation$ = new Subject<{ + messages: Message[]; + title?: string; + hideConversationList?: boolean; + }>(); const scope$ = new BehaviorSubject(scopes); @@ -104,8 +108,16 @@ export function createService({ ); }, conversations: { - openNewConversation: ({ messages, title }: { messages: Message[]; title?: string }) => { - predefinedConversation$.next({ messages, title }); + openNewConversation: ({ + messages, + title, + hideConversationList = false, + }: { + messages: Message[]; + title?: string; + hideConversationList?: boolean; + }) => { + predefinedConversation$.next({ messages, title, hideConversationList }); }, predefinedConversation$: predefinedConversation$.asObservable(), }, diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/types.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/public/types.ts index becc21f59c5f4..72517df5bffbc 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/types.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/types.ts @@ -91,8 +91,16 @@ export interface ObservabilityAIAssistantChatService { } export interface ObservabilityAIAssistantConversationService { - openNewConversation: ({}: { messages: Message[]; title?: string }) => void; - predefinedConversation$: Observable<{ messages: Message[]; title?: string }>; + openNewConversation: ({}: { + messages: Message[]; + title?: string; + hideConversationList?: boolean; + }) => void; + predefinedConversation$: Observable<{ + messages: Message[]; + title?: string; + hideConversationList?: boolean; + }>; } export interface ObservabilityAIAssistantService { diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/functions/route.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/functions/route.ts index 8a61248d4e70e..c402a0506736f 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/functions/route.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/functions/route.ts @@ -68,7 +68,7 @@ const getFunctionsRoute = createObservabilityAIAssistantServerRoute({ systemMessage: getSystemMessageFromInstructions({ applicationInstructions: functionClient.getInstructions(), userInstructions, - adHocInstructions: [], + adHocInstructions: functionClient.getAdhocInstructions(), availableFunctionNames, }), }; diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/chat_function_client/index.test.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/chat_function_client/index.test.ts index 3d83c470de0c5..0d911b497cbbb 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/chat_function_client/index.test.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/chat_function_client/index.test.ts @@ -7,6 +7,7 @@ import dedent from 'dedent'; import { ChatFunctionClient, GET_DATA_ON_SCREEN_FUNCTION_NAME } from '.'; import { FunctionVisibility } from '../../../common/functions/types'; +import { AdHocInstruction } from '../../../common/types'; describe('chatFunctionClient', () => { describe('when executing a function with invalid arguments', () => { @@ -86,6 +87,7 @@ describe('chatFunctionClient', () => { ]); const functions = client.getFunctions(); + const adHocInstructions = client.getAdhocInstructions(); expect(functions[0]).toEqual({ definition: { @@ -97,7 +99,7 @@ describe('chatFunctionClient', () => { respond: expect.any(Function), }); - expect(functions[0].definition.description).toContain( + expect(adHocInstructions[0].text).toContain( dedent(`my_dummy_data: My dummy data my_other_dummy_data: My other dummy data `) @@ -128,4 +130,52 @@ describe('chatFunctionClient', () => { }); }); }); + + describe('when adhoc instructions are provided', () => { + let client: ChatFunctionClient; + + beforeEach(() => { + client = new ChatFunctionClient([]); + }); + + describe('register an adhoc Instruction', () => { + it('should register a new adhoc instruction', () => { + const adhocInstruction: AdHocInstruction = { + text: 'Test adhoc instruction', + instruction_type: 'application_instruction', + }; + + client.registerAdhocInstruction(adhocInstruction); + + expect(client.getAdhocInstructions()).toContainEqual(adhocInstruction); + }); + }); + + describe('retrieve adHoc instructions', () => { + it('should return all registered adhoc instructions', () => { + const firstAdhocInstruction: AdHocInstruction = { + text: 'First adhoc instruction', + instruction_type: 'application_instruction', + }; + + const secondAdhocInstruction: AdHocInstruction = { + text: 'Second adhoc instruction', + instruction_type: 'application_instruction', + }; + + client.registerAdhocInstruction(firstAdhocInstruction); + client.registerAdhocInstruction(secondAdhocInstruction); + + const adhocInstructions = client.getAdhocInstructions(); + + expect(adhocInstructions).toEqual([firstAdhocInstruction, secondAdhocInstruction]); + }); + + it('should return an empty array if no adhoc instructions are registered', () => { + const adhocInstructions = client.getAdhocInstructions(); + + expect(adhocInstructions).toEqual([]); + }); + }); + }); }); diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/chat_function_client/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/chat_function_client/index.ts index 97def121e8593..ad4b7f0a4fc92 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/chat_function_client/index.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/chat_function_client/index.ts @@ -10,13 +10,18 @@ import Ajv, { type ErrorObject, type ValidateFunction } from 'ajv'; import dedent from 'dedent'; import { compact, keyBy } from 'lodash'; import { FunctionVisibility, type FunctionResponse } from '../../../common/functions/types'; -import type { Message, ObservabilityAIAssistantScreenContextRequest } from '../../../common/types'; +import type { + AdHocInstruction, + Message, + ObservabilityAIAssistantScreenContextRequest, +} from '../../../common/types'; import { filterFunctionDefinitions } from '../../../common/utils/filter_function_definitions'; import type { FunctionCallChatFunction, FunctionHandler, FunctionHandlerRegistry, InstructionOrCallback, + RegisterAdHocInstruction, RegisterFunction, RegisterInstruction, } from '../types'; @@ -35,6 +40,8 @@ export const GET_DATA_ON_SCREEN_FUNCTION_NAME = 'get_data_on_screen'; export class ChatFunctionClient { private readonly instructions: InstructionOrCallback[] = []; + private readonly adhocInstructions: AdHocInstruction[] = []; + private readonly functionRegistry: FunctionHandlerRegistry = new Map(); private readonly validators: Map = new Map(); @@ -49,9 +56,7 @@ export class ChatFunctionClient { this.registerFunction( { name: GET_DATA_ON_SCREEN_FUNCTION_NAME, - description: dedent(`Get data that is on the screen: - ${allData.map((data) => `${data.name}: ${data.description}`).join('\n')} - `), + description: `Retrieve the structured data of content currently visible on the user's screen. Use this tool to understand what the user is viewing at this moment to provide more accurate and context-aware responses to their questions.`, visibility: FunctionVisibility.AssistantOnly, parameters: { type: 'object', @@ -75,6 +80,13 @@ export class ChatFunctionClient { }; } ); + + this.registerAdhocInstruction({ + text: `The ${GET_DATA_ON_SCREEN_FUNCTION_NAME} function will retrieve specific content from the user's screen by specifying a data key. Use this tool to provide context-aware responses. Available data: ${dedent( + allData.map((data) => `${data.name}: ${data.description}`).join('\n') + )}`, + instruction_type: 'application_instruction', + }); } this.actions.forEach((action) => { @@ -95,6 +107,10 @@ export class ChatFunctionClient { this.instructions.push(instruction); }; + registerAdhocInstruction: RegisterAdHocInstruction = (instruction: AdHocInstruction) => { + this.adhocInstructions.push(instruction); + }; + validate(name: string, parameters: unknown) { const validator = this.validators.get(name)!; if (!validator) { @@ -111,6 +127,10 @@ export class ChatFunctionClient { return this.instructions; } + getAdhocInstructions(): AdHocInstruction[] { + return this.adhocInstructions; + } + hasAction(name: string) { return !!this.actions.find((action) => action.name === name)!; } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.test.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.test.ts index 0476bda1af8a2..8da2a0d843b11 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.test.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.test.ts @@ -124,6 +124,7 @@ describe('Observability AI Assistant client', () => { getActions: jest.fn(), validate: jest.fn(), getInstructions: jest.fn(), + getAdhocInstructions: jest.fn(), } as any; let llmSimulator: LlmSimulator; @@ -173,6 +174,7 @@ describe('Observability AI Assistant client', () => { knowledgeBaseServiceMock.getUserInstructions.mockResolvedValue([]); functionClientMock.getInstructions.mockReturnValue(['system']); + functionClientMock.getAdhocInstructions.mockReturnValue([]); return new ObservabilityAIAssistantClient({ actionsClient: actionsClientMock, diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.ts index a050edc8008fb..162220ec7a7f1 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.ts @@ -47,12 +47,12 @@ import { } from '../../../common/conversation_complete'; import { CompatibleJSONSchema } from '../../../common/functions/types'; import { + AdHocInstruction, type Conversation, type ConversationCreateRequest, type ConversationUpdateRequest, type KnowledgeBaseEntry, type Message, - type AdHocInstruction, } from '../../../common/types'; import { withoutTokenCountEvents } from '../../../common/utils/without_token_count_events'; import { CONTEXT_FUNCTION_NAME } from '../../functions/context'; @@ -210,6 +210,9 @@ export class ObservabilityAIAssistantClient { const userInstructions$ = from(this.getKnowledgeBaseUserInstructions()).pipe(shareReplay()); + const registeredAdhocInstructions = functionClient.getAdhocInstructions(); + const allAdHocInstructions = adHocInstructions.concat(registeredAdhocInstructions); + // from the initial messages, override any system message with // the one that is based on the instructions (registered, request, kb) const messagesWithUpdatedSystemMessage$ = userInstructions$.pipe( @@ -219,7 +222,7 @@ export class ObservabilityAIAssistantClient { getSystemMessageFromInstructions({ applicationInstructions: functionClient.getInstructions(), userInstructions, - adHocInstructions, + adHocInstructions: allAdHocInstructions, availableFunctionNames: functionClient .getFunctions() .map((fn) => fn.definition.name), diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/operators/continue_conversation.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/operators/continue_conversation.ts index 66204c96f31cb..4c55d32362878 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/operators/continue_conversation.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/operators/continue_conversation.ts @@ -178,7 +178,7 @@ export function continueConversation({ chat, signal, functionCallsLeft, - adHocInstructions, + adHocInstructions = [], userInstructions, logger, disableFunctions, @@ -213,11 +213,14 @@ export function continueConversation({ disableFunctions, }); + const registeredAdhocInstructions = functionClient.getAdhocInstructions(); + const allAdHocInstructions = adHocInstructions.concat(registeredAdhocInstructions); + const messagesWithUpdatedSystemMessage = replaceSystemMessage( getSystemMessageFromInstructions({ applicationInstructions: functionClient.getInstructions(), userInstructions, - adHocInstructions, + adHocInstructions: allAdHocInstructions, availableFunctionNames: definitions.map((def) => def.name), }), initialMessages diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/types.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/types.ts index b00da8d6518fa..2df3f36163972 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/types.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/types.ts @@ -18,6 +18,7 @@ import type { Message, ObservabilityAIAssistantScreenContextRequest, InstructionOrPlainText, + AdHocInstruction, } from '../../common/types'; import type { ObservabilityAIAssistantRouteHandlerResources } from '../routes/types'; import { ChatFunctionClient } from './chat_function_client'; @@ -76,6 +77,8 @@ export type RegisterInstructionCallback = ({ export type RegisterInstruction = (...instruction: InstructionOrCallback[]) => void; +export type RegisterAdHocInstruction = (...instruction: AdHocInstruction[]) => void; + export type RegisterFunction = < TParameters extends CompatibleJSONSchema = any, TResponse extends FunctionResponse = any, diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/util/get_system_message_from_instructions.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/util/get_system_message_from_instructions.ts index b2797577883ba..570449673084b 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/util/get_system_message_from_instructions.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/util/get_system_message_from_instructions.ts @@ -45,7 +45,7 @@ export function getSystemMessageFromInstructions({ const adHocInstructionsWithId = adHocInstructions.map((adHocInstruction) => ({ ...adHocInstruction, - doc_id: adHocInstruction.doc_id ?? v4(), + doc_id: adHocInstruction?.doc_id ?? v4(), })); // split ad hoc instructions into user instructions and application instructions diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/tsconfig.json b/x-pack/plugins/observability_solution/observability_ai_assistant/tsconfig.json index 7c2f2212ee946..750bf69477653 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/tsconfig.json +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/tsconfig.json @@ -14,7 +14,6 @@ ], "kbn_references": [ "@kbn/i18n", - "@kbn/inference-plugin", "@kbn/logging", "@kbn/kibana-utils-plugin", "@kbn/core-analytics-browser", @@ -44,9 +43,9 @@ "@kbn/serverless", "@kbn/core-elasticsearch-server", "@kbn/core-ui-settings-server", - "@kbn/inference-plugin", "@kbn/management-settings-ids", "@kbn/ai-assistant-common", + "@kbn/inference-common", ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/common/convert_messages_for_inference.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/common/convert_messages_for_inference.ts index 1dc8638626d0b..7ab9516440988 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/common/convert_messages_for_inference.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/common/convert_messages_for_inference.ts @@ -10,8 +10,8 @@ import { AssistantMessage, Message as InferenceMessage, MessageRole as InferenceMessageRole, - generateFakeToolCallId, -} from '@kbn/inference-plugin/common'; +} from '@kbn/inference-common'; +import { generateFakeToolCallId } from '@kbn/inference-plugin/common'; export function convertMessagesForInference(messages: Message[]): InferenceMessage[] { const inferenceMessages: InferenceMessage[] = []; diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/nav_control/index.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/nav_control/index.tsx index 883317c02274f..b6095ac595cea 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/nav_control/index.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/nav_control/index.tsx @@ -103,9 +103,12 @@ export function NavControl() { }; }, [service.conversations.predefinedConversation$]); - const { messages, title } = useObservable(service.conversations.predefinedConversation$) ?? { + const { messages, title, hideConversationList } = useObservable( + service.conversations.predefinedConversation$ + ) ?? { messages: [], title: undefined, + hideConversationList: false, }; const theme = useTheme(); @@ -171,6 +174,7 @@ export function NavControl() { ) ); }} + hideConversationList={hideConversationList} /> ) : undefined} diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/index.ts index 3643c54365248..210dee20339af 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/index.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/index.ts @@ -5,11 +5,8 @@ * 2.0. */ -import { - correctCommonEsqlMistakes, - isChatCompletionChunkEvent, - isOutputEvent, -} from '@kbn/inference-plugin/common'; +import { isChatCompletionChunkEvent, isOutputEvent } from '@kbn/inference-common'; +import { correctCommonEsqlMistakes } from '@kbn/inference-plugin/common'; import { naturalLanguageToEsql } from '@kbn/inference-plugin/server'; import { FunctionVisibility, diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/rule_connector/index.test.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/rule_connector/index.test.ts index 479ffeaa40f4f..de02e4cf841ce 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/rule_connector/index.test.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/rule_connector/index.test.ts @@ -91,6 +91,7 @@ describe('observabilityAIAssistant rule_connector', () => { getFunctionClient: async () => ({ getFunctions: () => [], getInstructions: () => [], + getAdhocInstructions: () => [], }), }, context: { diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/rule_connector/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/rule_connector/index.ts index 19f1408275e1f..59b883fef9c18 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/rule_connector/index.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/rule_connector/index.ts @@ -230,7 +230,7 @@ If available, include the link of the conversation at the end of your answer.` availableFunctionNames: functionClient.getFunctions().map((fn) => fn.definition.name), applicationInstructions: functionClient.getInstructions(), userInstructions: [], - adHocInstructions: [], + adHocInstructions: functionClient.getAdhocInstructions(), }), }, }, diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/tsconfig.json b/x-pack/plugins/observability_solution/observability_ai_assistant_app/tsconfig.json index af04a677f5e94..6608799caaf61 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/tsconfig.json +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/tsconfig.json @@ -69,6 +69,7 @@ "@kbn/cloud-plugin", "@kbn/logs-data-access-plugin", "@kbn/ai-assistant-common", + "@kbn/inference-common", ], "exclude": [ "target/**/*" diff --git a/x-pack/plugins/observability_solution/observability_logs_explorer/public/routes/main/main_route.tsx b/x-pack/plugins/observability_solution/observability_logs_explorer/public/routes/main/main_route.tsx index d1a2dc1e74439..49b25c29dad53 100644 --- a/x-pack/plugins/observability_solution/observability_logs_explorer/public/routes/main/main_route.tsx +++ b/x-pack/plugins/observability_solution/observability_logs_explorer/public/routes/main/main_route.tsx @@ -21,26 +21,17 @@ import { } from '../../state_machines/observability_logs_explorer/src'; import { LazyOriginInterpreter } from '../../state_machines/origin_interpreter/src/lazy_component'; import { ObservabilityLogsExplorerHistory } from '../../types'; -import { noBreadcrumbs, useBreadcrumbs } from '../../utils/breadcrumbs'; +import { useBreadcrumbs } from '../../utils/breadcrumbs'; import { useKbnUrlStateStorageFromRouterContext } from '../../utils/kbn_url_state_context'; import { useKibanaContextForPlugin } from '../../utils/use_kibana'; export const ObservabilityLogsExplorerMainRoute = () => { const { services } = useKibanaContextForPlugin(); - const { - logsExplorer, - serverless, - chrome, - notifications, - appParams, - analytics, - i18n, - theme, - logsDataAccess, - } = services; + const { logsExplorer, notifications, appParams, analytics, i18n, theme, logsDataAccess } = + services; const { history } = appParams; - useBreadcrumbs(noBreadcrumbs, chrome, serverless); + useBreadcrumbs(); const urlStateStorageContainer = useKbnUrlStateStorageFromRouterContext(); diff --git a/x-pack/plugins/observability_solution/observability_logs_explorer/public/utils/breadcrumbs.tsx b/x-pack/plugins/observability_solution/observability_logs_explorer/public/utils/breadcrumbs.tsx index 5e84404239866..4c63476e9507d 100644 --- a/x-pack/plugins/observability_solution/observability_logs_explorer/public/utils/breadcrumbs.tsx +++ b/x-pack/plugins/observability_solution/observability_logs_explorer/public/utils/breadcrumbs.tsx @@ -5,71 +5,27 @@ * 2.0. */ -import { EuiBreadcrumb } from '@elastic/eui'; -import type { ChromeStart } from '@kbn/core-chrome-browser'; -import { - LOGS_APP_ID, - OBSERVABILITY_LOGS_EXPLORER_APP_ID, - OBSERVABILITY_OVERVIEW_APP_ID, -} from '@kbn/deeplinks-observability'; +import { LOGS_APP_ID, OBSERVABILITY_LOGS_EXPLORER_APP_ID } from '@kbn/deeplinks-observability'; import { useLinkProps } from '@kbn/observability-shared-plugin/public'; -import type { ServerlessPluginStart } from '@kbn/serverless/public'; -import { useEffect } from 'react'; -import { - logsExplorerAppTitle, - logsAppTitle, - observabilityAppTitle, -} from '../../common/translations'; +import { useMemo } from 'react'; +import { useBreadcrumbs as observabilityUseBreadcrumbs } from '@kbn/observability-shared-plugin/public'; +import { logsExplorerAppTitle, logsAppTitle } from '../../common/translations'; -export const useBreadcrumbs = ( - breadcrumbs: EuiBreadcrumb[], - chromeService: ChromeStart, - serverlessService?: ServerlessPluginStart -) => { - const observabilityLinkProps = useLinkProps({ app: OBSERVABILITY_OVERVIEW_APP_ID }); +export const useBreadcrumbs = () => { const logsLinkProps = useLinkProps({ app: LOGS_APP_ID }); const logsExplorerLinkProps = useLinkProps({ app: OBSERVABILITY_LOGS_EXPLORER_APP_ID }); + const classicCrumbs = useMemo(() => { + return [ + { + text: logsAppTitle, + ...logsLinkProps, + }, + { + text: logsExplorerAppTitle, + ...logsExplorerLinkProps, + }, + ]; + }, [logsExplorerLinkProps, logsLinkProps]); - useEffect(() => { - setBreadcrumbs( - serverlessService - ? breadcrumbs - : [ - { - text: observabilityAppTitle, - ...observabilityLinkProps, - }, - { - text: logsAppTitle, - ...logsLinkProps, - }, - { - text: logsExplorerAppTitle, - ...logsExplorerLinkProps, - }, - ...breadcrumbs, - ], - chromeService, - serverlessService - ); - }, [breadcrumbs, chromeService, serverlessService]); // eslint-disable-line react-hooks/exhaustive-deps + observabilityUseBreadcrumbs(classicCrumbs, { classicOnly: true }); }; - -export function setBreadcrumbs( - breadcrumbs: EuiBreadcrumb[], - chromeService: ChromeStart, - serverlessService?: ServerlessPluginStart -) { - chromeService.docTitle.change(getDocTitle(breadcrumbs)); - if (serverlessService) { - serverlessService.setBreadcrumbs(breadcrumbs); - } else if (chromeService) { - chromeService.setBreadcrumbs(breadcrumbs); - } -} - -export function getDocTitle(breadcrumbs: EuiBreadcrumb[]) { - return breadcrumbs.map(({ text }) => text as string).reverse(); -} - -export const noBreadcrumbs: EuiBreadcrumb[] = []; diff --git a/x-pack/plugins/observability_solution/observability_logs_explorer/tsconfig.json b/x-pack/plugins/observability_solution/observability_logs_explorer/tsconfig.json index be2b3c9efdff6..5a2f18aa4249a 100644 --- a/x-pack/plugins/observability_solution/observability_logs_explorer/tsconfig.json +++ b/x-pack/plugins/observability_solution/observability_logs_explorer/tsconfig.json @@ -13,7 +13,6 @@ "kbn_references": [ "@kbn/config-schema", "@kbn/core", - "@kbn/core-chrome-browser", "@kbn/core-mount-utils-browser-internal", "@kbn/core-notifications-browser", "@kbn/data-plugin", diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/auto_detect/auto_detect_panel.tsx b/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/auto_detect/auto_detect_panel.tsx index 5d62f1060b50e..d12f0cae583f4 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/auto_detect/auto_detect_panel.tsx +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/auto_detect/auto_detect_panel.tsx @@ -23,6 +23,7 @@ import { } from '@kbn/deeplinks-observability/locators'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { DASHBOARD_APP_LOCATOR } from '@kbn/deeplinks-analytics'; +import { ASSET_DETAILS_LOCATOR_ID } from '@kbn/observability-shared-plugin/common'; import { getAutoDetectCommand } from './get_auto_detect_command'; import { DASHBOARDS, useOnboardingFlow } from './use_onboarding_flow'; import { ProgressIndicator } from '../shared/progress_indicator'; @@ -66,6 +67,7 @@ export const AutoDetectPanel: FunctionComponent = () => { (integration) => integration.installSource === 'custom' ); const dashboardLocator = share.url.locators.get(DASHBOARD_APP_LOCATOR); + const assetDetailsLocator = share.url.locators.get(ASSET_DETAILS_LOCATOR_ID); return ( @@ -147,88 +149,133 @@ export const AutoDetectPanel: FunctionComponent = () => { installedIntegrations.length > 0 ? ( <> - {registryIntegrations.map((integration) => ( - - ) : ( - - ) - } - title={i18n.translate( - 'xpack.observability_onboarding.autoDetectPanel.h3.getStartedWithNginxLabel', - { - defaultMessage: 'Get started with {title}', - values: { title: integration.title }, - } - )} - isDisabled={status !== 'dataReceived'} - initialIsOpen - > - asset.type === 'dashboard') - .map((asset) => { - const dashboard = DASHBOARDS[asset.id as keyof typeof DASHBOARDS]; - const href = - dashboardLocator?.getRedirectUrl({ - dashboardId: asset.id, - }) ?? ''; + {registryIntegrations + .slice() + /** + * System integration should always be on top + */ + .sort((a, b) => (a.pkgName === 'system' ? -1 : 0)) + .map((integration) => { + let actionLinks; - return { - id: asset.id, - title: - dashboard.type === 'metrics' - ? i18n.translate( - 'xpack.observability_onboarding.autoDetectPanel.exploreMetricsDataTitle', - { - defaultMessage: - 'Overview your metrics data with this pre-made dashboard', - } - ) - : i18n.translate( - 'xpack.observability_onboarding.autoDetectPanel.exploreLogsDataTitle', + switch (integration.pkgName) { + case 'system': + actionLinks = + assetDetailsLocator !== undefined + ? [ + { + id: 'inventory-host-details', + title: i18n.translate( + 'xpack.observability_onboarding.autoDetectPanel.systemOverviewTitle', { defaultMessage: - 'Overview your logs data with this pre-made dashboard', + 'Overview your system health within the Hosts Inventory', } ), - label: - dashboard.type === 'metrics' - ? i18n.translate( - 'xpack.observability_onboarding.autoDetectPanel.exploreMetricsDataLabel', + label: i18n.translate( + 'xpack.observability_onboarding.autoDetectPanel.systemOverviewLabel', { defaultMessage: 'Explore metrics data', } - ) - : i18n.translate( - 'xpack.observability_onboarding.autoDetectPanel.exploreLogsDataLabel', - { - defaultMessage: 'Explore logs data', - } ), - href, - }; - })} - /> - - ))} + href: assetDetailsLocator.getRedirectUrl({ + assetType: 'host', + assetId: integration.metadata?.hostname, + }), + }, + ] + : []; + break; + default: + actionLinks = + dashboardLocator !== undefined + ? integration.kibanaAssets + .filter((asset) => asset.type === 'dashboard') + .map((asset) => { + const dashboard = + DASHBOARDS[asset.id as keyof typeof DASHBOARDS]; + const href = dashboardLocator.getRedirectUrl({ + dashboardId: asset.id, + }); + + return { + id: asset.id, + title: + dashboard.type === 'metrics' + ? i18n.translate( + 'xpack.observability_onboarding.autoDetectPanel.exploreMetricsDataTitle', + { + defaultMessage: + 'Overview your metrics data with this pre-made dashboard', + } + ) + : i18n.translate( + 'xpack.observability_onboarding.autoDetectPanel.exploreLogsDataTitle', + { + defaultMessage: + 'Overview your logs data with this pre-made dashboard', + } + ), + label: + dashboard.type === 'metrics' + ? i18n.translate( + 'xpack.observability_onboarding.autoDetectPanel.exploreMetricsDataLabel', + { + defaultMessage: 'Explore metrics data', + } + ) + : i18n.translate( + 'xpack.observability_onboarding.autoDetectPanel.exploreLogsDataLabel', + { + defaultMessage: 'Explore logs data', + } + ), + href, + }; + }) + : []; + } + + return ( + + ) : ( + + ) + } + title={i18n.translate( + 'xpack.observability_onboarding.autoDetectPanel.h3.getStartedWithNginxLabel', + { + defaultMessage: 'Get started with {title}', + values: { title: integration.title }, + } + )} + isDisabled={status !== 'dataReceived'} + initialIsOpen + > + + + ); + })} {customIntegrations.length > 0 && ( ), + doc: ( + + {i18n.translate( + 'xpack.observability_onboarding.otelKubernetesPanel.certmanagerDocsLinkLabel', + { defaultMessage: 'in our documentation' } + )} + + ), }} />{' '} @@ -213,17 +225,33 @@ helm install opentelemetry-kube-stack open-telemetry/opentelemetry-kube-stack \\ ]} /> - - {`apiVersion: v1 -kind: Pod + + {`# To annotate specific deployment Pods modify its manifest +apiVersion: apps/v1 +kind: Deployment metadata: - name: my-app - annotations: - instrumentation.opentelemetry.io/inject-${idSelected}: "${namespace}/elastic-instrumentation" + name: myapp spec: - containers: - - name: my-app - image: my-app:latest`} + ... + template: + metadata: + annotations: + instrumentation.opentelemetry.io/inject-${idSelected}: "${namespace}/elastic-instrumentation" + ... + spec: + containers: + - image: myapplication-image + name: app + ... + +# To annotate all resources in a namespace +kubectl annotate namespace my-namespace instrumentation.opentelemetry.io/inject-${idSelected}="${namespace}/elastic-instrumentation" + +# Restart your deployment +kubectl rollout restart deployment myapp -n my-namespace + +# Check annotations have been applied correctly and auto-instrumentation library is injected +kubectl describe pod -n my-namespace`} diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/assets/auto_detect.sh b/x-pack/plugins/observability_solution/observability_onboarding/public/assets/auto_detect.sh index c315ef483d9d6..3b45ed3baf626 100755 --- a/x-pack/plugins/observability_solution/observability_onboarding/public/assets/auto_detect.sh +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/assets/auto_detect.sh @@ -257,7 +257,15 @@ install_integrations() { local install_integrations_api_body_string="" for item in "${selected_known_integrations_array[@]}"; do - install_integrations_api_body_string+="$item\tregistry\n" + local metadata="" + + case "$item" in + "system") + metadata="\t$(hostname | tr '[:upper:]' '[:lower:]')" + ;; + esac + + install_integrations_api_body_string+="$item\tregistry$metadata\n" done for item in "${selected_unknown_log_file_pattern_array[@]}" "${custom_log_file_path_list_array[@]}"; do diff --git a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/flow/route.ts b/x-pack/plugins/observability_solution/observability_onboarding/server/routes/flow/route.ts index d6575f8751c4a..5aa2bc489ab59 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/flow/route.ts +++ b/x-pack/plugins/observability_solution/observability_onboarding/server/routes/flow/route.ts @@ -392,9 +392,16 @@ const integrationsInstallRoute = createObservabilityOnboardingServerRoute({ }, }); +interface InstalledSystemIntegrationMetadata { + hostname: string; +} + +type RegistryIntegrationMetadata = InstalledSystemIntegrationMetadata; + export interface RegistryIntegrationToInstall { pkgName: string; installSource: 'registry'; + metadata?: RegistryIntegrationMetadata; } export interface CustomIntegrationToInstall { pkgName: string; @@ -426,6 +433,7 @@ async function ensureInstalledIntegrations( dataStreams: packageInfo.data_streams?.map(({ type, dataset }) => ({ type, dataset })) ?? [], kibanaAssets: pkg.installed_kibana, + metadata: integration.metadata, }; } @@ -482,7 +490,8 @@ async function ensureInstalledIntegrations( * Example input: * * ```text - * system registry + * system registry hostname + * nginx registry * product_service custom /path/to/access.log * product_service custom /path/to/error.log * checkout_service custom /path/to/access.log @@ -495,42 +504,55 @@ function parseIntegrationsTSV(tsv: string) { .trim() .split('\n') .map((line) => line.split('\t', 3)) - .reduce>( - (acc, [pkgName, installSource, logFilePath]) => { - const key = `${pkgName}-${installSource}`; - if (installSource === 'registry') { - if (logFilePath) { - throw new Error(`Integration '${pkgName}' does not support a file path`); - } - acc[key] = { - pkgName, - installSource, - }; - return acc; - } else if (installSource === 'custom') { - if (!logFilePath) { - throw new Error(`Missing file path for integration: ${pkgName}`); - } - // Append file path if integration is already in the list - const existing = acc[key]; - if (existing && existing.installSource === 'custom') { - existing.logFilePaths.push(logFilePath); - return acc; - } - acc[key] = { - pkgName, - installSource, - logFilePaths: [logFilePath], - }; + .reduce>((acc, [pkgName, installSource, parameter]) => { + const key = `${pkgName}-${installSource}`; + if (installSource === 'registry') { + const metadata = parseRegistryIntegrationMetadata(pkgName, parameter); + + acc[key] = { + pkgName, + installSource, + metadata, + }; + return acc; + } else if (installSource === 'custom') { + if (!parameter) { + throw new Error(`Missing file path for integration: ${pkgName}`); + } + // Append file path if integration is already in the list + const existing = acc[key]; + if (existing && existing.installSource === 'custom') { + existing.logFilePaths.push(parameter); return acc; } - throw new Error(`Invalid install source: ${installSource}`); - }, - {} - ) + acc[key] = { + pkgName, + installSource, + logFilePaths: [parameter], + }; + return acc; + } + throw new Error(`Invalid install source: ${installSource}`); + }, {}) ); } +function parseRegistryIntegrationMetadata( + pkgName: string, + parameter: string +): RegistryIntegrationMetadata | undefined { + switch (pkgName) { + case 'system': + if (!parameter) { + throw new Error('Missing hostname for System integration'); + } + + return { hostname: parameter }; + default: + return undefined; + } +} + const generateAgentConfigTar = ({ elasticsearchUrl, installedIntegrations, diff --git a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/types.ts b/x-pack/plugins/observability_solution/observability_onboarding/server/routes/types.ts index c9cded0805f65..4b35272eaa330 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/types.ts +++ b/x-pack/plugins/observability_solution/observability_onboarding/server/routes/types.ts @@ -52,25 +52,30 @@ export interface ObservabilityOnboardingRouteCreateOptions { }; } -export const IntegrationRT = t.type({ - installSource: t.union([t.literal('registry'), t.literal('custom')]), - pkgName: t.string, - pkgVersion: t.string, - title: t.string, - config: t.string, - dataStreams: t.array( - t.type({ - type: t.string, - dataset: t.string, - }) - ), - kibanaAssets: t.array( - t.type({ - type: t.string, - id: t.string, - }) - ), -}); +export const IntegrationRT = t.intersection([ + t.type({ + installSource: t.union([t.literal('registry'), t.literal('custom')]), + pkgName: t.string, + pkgVersion: t.string, + title: t.string, + config: t.string, + dataStreams: t.array( + t.type({ + type: t.string, + dataset: t.string, + }) + ), + kibanaAssets: t.array( + t.type({ + type: t.string, + id: t.string, + }) + ), + }), + t.partial({ + metadata: t.type({ hostname: t.string }), + }), +]); export type InstalledIntegration = t.TypeOf; diff --git a/x-pack/plugins/observability_solution/observability_onboarding/server/saved_objects/observability_onboarding_status.ts b/x-pack/plugins/observability_solution/observability_onboarding/server/saved_objects/observability_onboarding_status.ts index c59bec0285266..03be370e6cf6b 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/server/saved_objects/observability_onboarding_status.ts +++ b/x-pack/plugins/observability_solution/observability_onboarding/server/saved_objects/observability_onboarding_status.ts @@ -75,6 +75,13 @@ export const InstallIntegrationsStepPayloadSchema = schema.arrayOf( id: schema.string(), }) ), + metadata: schema.maybe( + schema.oneOf([ + schema.object({ + hostname: schema.string(), + }), + ]) + ), }) ); diff --git a/x-pack/plugins/observability_solution/observability_shared/common/entity/entity_types.ts b/x-pack/plugins/observability_solution/observability_shared/common/entity/entity_types.ts index b905f542d3473..4d8be9efc59c6 100644 --- a/x-pack/plugins/observability_solution/observability_shared/common/entity/entity_types.ts +++ b/x-pack/plugins/observability_solution/observability_shared/common/entity/entity_types.ts @@ -5,7 +5,25 @@ * 2.0. */ -export enum EntityType { - HOST = 'host', - CONTAINER = 'container', -} +const createKubernetesEntity = (base: T) => ({ + ecs: `kubernetes_${base}_ecs` as const, + semconv: `kubernetes_${base}_semconv` as const, +}); + +export const ENTITY_TYPES = { + HOST: 'host', + CONTAINER: 'container', + SERVICE: 'service', + KUBERNETES: { + CLUSTER: createKubernetesEntity('cluster'), + CONTAINER: createKubernetesEntity('container'), + CRONJOB: createKubernetesEntity('cron_job'), + DAEMONSET: createKubernetesEntity('daemon_set'), + DEPLOYMENT: createKubernetesEntity('deployment'), + JOB: createKubernetesEntity('job'), + NAMESPACE: createKubernetesEntity('namespace'), + NODE: createKubernetesEntity('node'), + POD: createKubernetesEntity('pod'), + STATEFULSET: createKubernetesEntity('stateful_set'), + }, +} as const; diff --git a/x-pack/plugins/observability_solution/observability_shared/common/entity/index.ts b/x-pack/plugins/observability_solution/observability_shared/common/entity/index.ts index 27bef43d5ff7a..adc07a2931b60 100644 --- a/x-pack/plugins/observability_solution/observability_shared/common/entity/index.ts +++ b/x-pack/plugins/observability_solution/observability_shared/common/entity/index.ts @@ -5,5 +5,5 @@ * 2.0. */ -export { EntityType } from './entity_types'; +export { ENTITY_TYPES } from './entity_types'; export { EntityDataStreamType } from './entity_data_stream_types'; diff --git a/x-pack/plugins/observability_solution/observability_shared/common/index.ts b/x-pack/plugins/observability_solution/observability_shared/common/index.ts index e9be61e8fde34..b4b7731d166b7 100644 --- a/x-pack/plugins/observability_solution/observability_shared/common/index.ts +++ b/x-pack/plugins/observability_solution/observability_shared/common/index.ts @@ -193,6 +193,7 @@ export type { export { ServiceOverviewLocatorDefinition, + SERVICE_OVERVIEW_LOCATOR_ID, TransactionDetailsByNameLocatorDefinition, ASSET_DETAILS_FLYOUT_LOCATOR_ID, AssetDetailsFlyoutLocatorDefinition, @@ -218,4 +219,4 @@ export { export { COMMON_OBSERVABILITY_GROUPING } from './embeddable_grouping'; -export { EntityType, EntityDataStreamType } from './entity'; +export { ENTITY_TYPES, EntityDataStreamType } from './entity'; diff --git a/x-pack/plugins/observability_solution/observability_shared/common/locators/apm/service_overview_locator.ts b/x-pack/plugins/observability_solution/observability_shared/common/locators/apm/service_overview_locator.ts index 2a4e8aac330ec..e216640f31b4f 100644 --- a/x-pack/plugins/observability_solution/observability_shared/common/locators/apm/service_overview_locator.ts +++ b/x-pack/plugins/observability_solution/observability_shared/common/locators/apm/service_overview_locator.ts @@ -16,9 +16,10 @@ export interface ServiceOverviewParams extends SerializableRecord { } export type ServiceOverviewLocator = LocatorPublic; +export const SERVICE_OVERVIEW_LOCATOR_ID = 'serviceOverviewLocator'; export class ServiceOverviewLocatorDefinition implements LocatorDefinition { - public readonly id = 'serviceOverviewLocator'; + public readonly id = SERVICE_OVERVIEW_LOCATOR_ID; public readonly getLocation = async ({ rangeFrom, diff --git a/x-pack/plugins/observability_solution/observability_shared/public/hooks/use_breadcrumbs.test.tsx b/x-pack/plugins/observability_solution/observability_shared/public/hooks/use_breadcrumbs.test.tsx index 15849039bde8b..c166057df0304 100644 --- a/x-pack/plugins/observability_solution/observability_shared/public/hooks/use_breadcrumbs.test.tsx +++ b/x-pack/plugins/observability_solution/observability_shared/public/hooks/use_breadcrumbs.test.tsx @@ -11,12 +11,18 @@ import { MemoryRouter } from 'react-router-dom'; import { CoreStart } from '@kbn/core/public'; import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; import { useBreadcrumbs } from './use_breadcrumbs'; +import { BehaviorSubject } from 'rxjs'; +import { ChromeStyle } from '@kbn/core-chrome-browser'; const setBreadcrumbs = jest.fn(); const setTitle = jest.fn(); const kibanaServices = { application: { getUrlForApp: () => {}, navigateToApp: () => {} }, - chrome: { setBreadcrumbs, docTitle: { change: setTitle } }, + chrome: { + setBreadcrumbs, + docTitle: { change: setTitle }, + getChromeStyle$: () => new BehaviorSubject('classic').asObservable(), + }, uiSettings: { get: () => true }, settings: { client: { get: () => true } }, } as unknown as Partial; @@ -61,9 +67,15 @@ describe('useBreadcrumbs', () => { it('sets the overview breadcrumb', () => { renderHook(() => useBreadcrumbs([]), { wrapper: Wrapper }); - expect(setBreadcrumbs).toHaveBeenCalledWith([ - { href: '/overview', onClick: expect.any(Function), text: 'Observability' }, - ]); + expect(setBreadcrumbs).toHaveBeenCalledWith( + [{ href: '/overview', onClick: expect.any(Function), text: 'Observability' }], + { + project: { + absolute: true, + value: [{ href: '/overview', onClick: expect.any(Function), text: 'Observability' }], + }, + } + ); }); it('sets the overview title', () => { @@ -86,17 +98,29 @@ describe('useBreadcrumbs', () => { { wrapper: Wrapper } ); - expect(setBreadcrumbs).toHaveBeenCalledWith([ - { href: '/overview', onClick: expect.any(Function), text: 'Observability' }, + expect(setBreadcrumbs).toHaveBeenCalledWith( + [ + { href: '/overview', onClick: expect.any(Function), text: 'Observability' }, + { + href: '/one', + onClick: expect.any(Function), + text: 'One', + }, + { + text: 'Two', + }, + ], { - href: '/one', - onClick: expect.any(Function), - text: 'One', - }, - { - text: 'Two', - }, - ]); + project: { + absolute: true, + value: [ + { href: '/overview', onClick: expect.any(Function), text: 'Observability' }, + { href: '/one', onClick: expect.any(Function), text: 'One' }, + { text: 'Two' }, + ], + }, + } + ); }); it('sets the title', () => { diff --git a/x-pack/plugins/observability_solution/observability_shared/public/hooks/use_breadcrumbs.ts b/x-pack/plugins/observability_solution/observability_shared/public/hooks/use_breadcrumbs.ts index 4137d541c4e39..5c9c0d3981bb0 100644 --- a/x-pack/plugins/observability_solution/observability_shared/public/hooks/use_breadcrumbs.ts +++ b/x-pack/plugins/observability_solution/observability_shared/public/hooks/use_breadcrumbs.ts @@ -11,8 +11,16 @@ import { MouseEvent, useEffect, useMemo } from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { ChromeBreadcrumbsAppendExtension } from '@kbn/core-chrome-browser'; import type { ServerlessPluginStart } from '@kbn/serverless/public'; +import useObservable from 'react-use/lib/useObservable'; import { useQueryParams } from './use_query_params'; +const OBSERVABILITY_TEXT = i18n.translate( + 'xpack.observabilityShared.breadcrumbs.observabilityLinkText', + { + defaultMessage: 'Observability', + } +); + function addClickHandlers( breadcrumbs: ChromeBreadcrumb[], navigateToHref?: (url: string) => Promise @@ -33,23 +41,49 @@ function addClickHandlers( } function getTitleFromBreadCrumbs(breadcrumbs: ChromeBreadcrumb[]) { - return breadcrumbs.map(({ text }) => text?.toString() ?? '').reverse(); + return breadcrumbs + .map(({ text }) => text?.toString() ?? '') + .reverse() + .concat([OBSERVABILITY_TEXT]); } +/** + * + * By default the breadcrumbs will be passed to either serverless.setBreadcrumbs or chrome.setBreadcrumbs depending on the + * environment. The breadcrumbs will *also* be passed to the project style breadcrumbs for stateful project style. We will use "project style" + * here to refer to serverless chrome and stateful project style chrome. Classic refers to stateful classic chrome. + * + * Project style breadcrumbs add a root crumb ("deployment" etc) and "nav crumbs" which are derived from the navigation structure. By default + * the "absolute" mode is used which means the breadcrumbs passed here will omit the navigation derived "nav crumbs". You can pass + * absoluteProjectStyleBreadcrumbs: false to include the 'smart' "nav crumbs". + * + * In classic mode (not project style) the 'Observability' crumb is added. + * + * You can also pass classicOnly to only set breadrumbs in the classic chrome context. This can be useful if your solution just wants to defer all project style crumbs to the "nav crumbs". + */ export const useBreadcrumbs = ( extraCrumbs: ChromeBreadcrumb[], options?: { app?: { id: string; label: string }; breadcrumbsAppendExtension?: ChromeBreadcrumbsAppendExtension; serverless?: ServerlessPluginStart; + absoluteProjectStyleBreadcrumbs?: boolean; + classicOnly?: boolean; } ) => { const params = useQueryParams(); const { app, breadcrumbsAppendExtension, serverless } = options ?? {}; + const absolute = options?.absoluteProjectStyleBreadcrumbs === false ? false : true; + const classicOnly = options?.classicOnly ?? false; const { services: { - chrome: { docTitle, setBreadcrumbs: chromeSetBreadcrumbs, setBreadcrumbsAppendExtension }, + chrome: { + docTitle, + setBreadcrumbs: chromeSetBreadcrumbs, + setBreadcrumbsAppendExtension, + getChromeStyle$, + }, application: { getUrlForApp, navigateToUrl }, }, } = useKibana<{ @@ -58,11 +92,27 @@ export const useBreadcrumbs = ( }>(); const setTitle = docTitle.change; const appPath = getUrlForApp(app?.id ?? 'observability-overview') ?? ''; + const chromeStyle = useObservable(getChromeStyle$()); - const setBreadcrumbs = useMemo( - () => serverless?.setBreadcrumbs ?? chromeSetBreadcrumbs, - [serverless, chromeSetBreadcrumbs] - ); + const setBreadcrumbs = useMemo(() => { + if (!serverless?.setBreadcrumbs) { + return (breadcrumbs: ChromeBreadcrumb[]) => + chromeSetBreadcrumbs( + breadcrumbs, + !classicOnly + ? { + project: { + value: breadcrumbs, + absolute, + }, + } + : undefined + ); + } + if (!classicOnly) + return (breadcrumbs: ChromeBreadcrumb[]) => + serverless?.setBreadcrumbs(breadcrumbs, { absolute }); + }, [serverless, classicOnly, absolute, chromeSetBreadcrumbs]); useEffect(() => { if (breadcrumbsAppendExtension) { @@ -76,15 +126,12 @@ export const useBreadcrumbs = ( }, [breadcrumbsAppendExtension, setBreadcrumbsAppendExtension]); useEffect(() => { - const breadcrumbs = serverless + const isProjectStyle = serverless || chromeStyle === 'project'; + const breadcrumbs = isProjectStyle ? extraCrumbs : [ { - text: - app?.label ?? - i18n.translate('xpack.observabilityShared.breadcrumbs.observabilityLinkText', { - defaultMessage: 'Observability', - }), + text: app?.label ?? OBSERVABILITY_TEXT, href: appPath + '/overview', }, ...extraCrumbs, @@ -94,11 +141,12 @@ export const useBreadcrumbs = ( setBreadcrumbs(addClickHandlers(breadcrumbs, navigateToUrl)); } if (setTitle) { - setTitle(getTitleFromBreadCrumbs(breadcrumbs)); + setTitle(getTitleFromBreadCrumbs(extraCrumbs)); } }, [ app?.label, appPath, + chromeStyle, extraCrumbs, navigateToUrl, params, diff --git a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/overview/slo_overview_grid.tsx b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/overview/slo_overview_grid.tsx index 1ca47e02f4df3..f452f77cb1da3 100644 --- a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/overview/slo_overview_grid.tsx +++ b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/overview/slo_overview_grid.tsx @@ -9,7 +9,6 @@ import React from 'react'; import { ALL_VALUE, HistoricalSummaryResponse, SLOWithSummaryResponse } from '@kbn/slo-schema'; import { Chart, - DARK_THEME, isMetricElementEvent, Metric, MetricTrendShape, @@ -73,12 +72,18 @@ const getSloChartData = ({ }; }; +const ROW_HEIGHT = 220; +const ITEMS_PER_ROW = 4; + export function SloCardChartList({ sloId }: { sloId: string }) { const { http: { basePath }, uiSettings, + charts, } = useKibana().services; + const baseTheme = charts.theme.useChartsBaseTheme(); + const [selectedSlo, setSelectedSlo] = React.useState(null); const kqlQuery = `slo.id:"${sloId}"`; @@ -89,6 +94,7 @@ export function SloCardChartList({ sloId }: { sloId: string }) { const { data: activeAlertsBySlo } = useFetchActiveAlerts({ sloIdsAndInstanceIds: [[sloId, ALL_VALUE]], + rangeFrom: 'now-24h', }); const { data: rulesBySlo } = useFetchRulesForSlo({ @@ -151,16 +157,24 @@ export function SloCardChartList({ sloId }: { sloId: string }) { ); } + const height = sloList?.results + ? ROW_HEIGHT * Math.ceil(sloList.results.length / ITEMS_PER_ROW) + : ROW_HEIGHT; + return ( <> -
- +
+ { if (isMetricElementEvent(d)) { const { columnIndex, rowIndex } = d; - const slo = sloList?.results[rowIndex * 4 + columnIndex]; + const slo = sloList?.results[rowIndex * ITEMS_PER_ROW + columnIndex]; setSelectedSlo(slo ?? null); } }} diff --git a/x-pack/plugins/observability_solution/slo/public/hooks/use_fetch_active_alerts.ts b/x-pack/plugins/observability_solution/slo/public/hooks/use_fetch_active_alerts.ts index 1f353e6a38558..6ad34d8c4dc86 100644 --- a/x-pack/plugins/observability_solution/slo/public/hooks/use_fetch_active_alerts.ts +++ b/x-pack/plugins/observability_solution/slo/public/hooks/use_fetch_active_alerts.ts @@ -20,6 +20,7 @@ type SloIdAndInstanceId = [string, string]; interface Params { sloIdsAndInstanceIds: SloIdAndInstanceId[]; shouldRefetch?: boolean; + rangeFrom?: string; } export interface UseFetchActiveAlerts { @@ -46,6 +47,7 @@ const EMPTY_ACTIVE_ALERTS_MAP = new ActiveAlerts(); export function useFetchActiveAlerts({ sloIdsAndInstanceIds = [], shouldRefetch = false, + rangeFrom = 'now-5m/m', }: Params): UseFetchActiveAlerts { const { http } = useKibana().services; @@ -63,7 +65,7 @@ export function useFetchActiveAlerts({ { range: { '@timestamp': { - gte: 'now-5m/m', + gte: rangeFrom, }, }, }, diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_details/slo_details.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_details/slo_details.tsx index 26cdf62b4f7b4..9a32c150e1b8c 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_details/slo_details.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_details/slo_details.tsx @@ -40,6 +40,7 @@ export function SloDetailsPage() { application: { navigateToUrl }, http: { basePath }, observabilityAIAssistant, + serverless, } = useKibana().services; const { ObservabilityPageTemplate } = usePluginContext(); const { hasAtLeast } = useLicense(); @@ -105,7 +106,7 @@ export function SloDetailsPage() { } }, [onPageReady, slo, isLoading]); - useBreadcrumbs(getBreadcrumbs(basePath, slo)); + useBreadcrumbs(getBreadcrumbs(basePath, slo), { serverless }); const isSloNotFound = !isLoading && slo === undefined; if (isSloNotFound) { diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/common/query_search_bar.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/common/query_search_bar.tsx index d238aacf1df60..394d8c303e953 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/common/query_search_bar.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/common/query_search_bar.tsx @@ -69,7 +69,7 @@ export const QuerySearchBar = memo( field.onChange(String(value?.query)); } else { field.onChange({ - ...(field.value ?? {}), + filters: field.value?.filters ?? [], kqlQuery: String(value?.query), }); } @@ -111,15 +111,27 @@ export const QuerySearchBar = memo( } onQuerySubmit={(value) => handleQueryChange(value.query, value.dateRange)} onFiltersUpdated={(filters) => { + const updatedFilters = filters.map((filter) => { + const { $state, meta, ...rest } = filter; + const query = filter?.query ? { ...filter.query } : { ...rest }; + return { + meta: { + ...meta, + alias: meta?.alias ?? JSON.stringify(query), + }, + query, + }; + }); + if (kqlQuerySchema.is(field.value)) { field.onChange({ - filters, + filters: updatedFilters, kqlQuery: field.value, }); } else { field.onChange({ - ...(field.value ?? {}), - filters, + kqlQuery: field.value?.kqlQuery ?? '', + filters: updatedFilters, }); } }} diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/slo_edit.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/slo_edit.tsx index aa008838a8b3c..7dcce93c0d003 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/slo_edit.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/slo_edit.tsx @@ -22,6 +22,7 @@ export function SloEditPage() { const { application: { navigateToUrl }, http: { basePath }, + serverless, } = useKibana().services; const { sloId } = useParams<{ sloId: string | undefined }>(); @@ -32,32 +33,35 @@ export function SloEditPage() { const hasRightLicense = hasAtLeast('platinum'); const { data: slo } = useFetchSloDetails({ sloId }); - useBreadcrumbs([ - { - href: basePath.prepend(paths.slos), - text: i18n.translate('xpack.slo.breadcrumbs.sloLabel', { - defaultMessage: 'SLOs', - }), - deepLinkId: 'slo', - }, - ...(!!slo - ? [ - { - href: basePath.prepend(paths.sloDetails(slo!.id)), - text: slo!.name, - }, - ] - : []), - { - text: slo - ? i18n.translate('xpack.slo.breadcrumbs.sloEditLabel', { - defaultMessage: 'Edit', - }) - : i18n.translate('xpack.slo.breadcrumbs.sloCreateLabel', { - defaultMessage: 'Create', - }), - }, - ]); + useBreadcrumbs( + [ + { + href: basePath.prepend(paths.slos), + text: i18n.translate('xpack.slo.breadcrumbs.sloLabel', { + defaultMessage: 'SLOs', + }), + deepLinkId: 'slo', + }, + ...(!!slo + ? [ + { + href: basePath.prepend(paths.sloDetails(slo!.id)), + text: slo!.name, + }, + ] + : []), + { + text: slo + ? i18n.translate('xpack.slo.breadcrumbs.sloEditLabel', { + defaultMessage: 'Edit', + }) + : i18n.translate('xpack.slo.breadcrumbs.sloCreateLabel', { + defaultMessage: 'Create', + }), + }, + ], + { serverless } + ); useEffect(() => { if (hasRightLicense === false || permissions?.hasAllReadRequested === false) { diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_outdated_definitions/index.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_outdated_definitions/index.tsx index a9afc480676c8..5a35061b464e5 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_outdated_definitions/index.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_outdated_definitions/index.tsx @@ -23,26 +23,30 @@ import { OutdatedSloSearchBar } from './outdated_slo_search_bar'; export function SlosOutdatedDefinitions() { const { http: { basePath }, + serverless, } = useKibana().services; const { data: permissions } = usePermissions(); const { ObservabilityPageTemplate } = usePluginContext(); const { hasAtLeast } = useLicense(); - useBreadcrumbs([ - { - href: basePath.prepend(paths.slos), - text: i18n.translate('xpack.slo.breadcrumbs.slosLinkText', { - defaultMessage: 'SLOs', - }), - deepLinkId: 'slo', - }, - { - text: i18n.translate('xpack.slo.breadcrumbs.slosOutdatedDefinitions', { - defaultMessage: 'Outdated SLO Definitions', - }), - }, - ]); + useBreadcrumbs( + [ + { + href: basePath.prepend(paths.slos), + text: i18n.translate('xpack.slo.breadcrumbs.slosLinkText', { + defaultMessage: 'SLOs', + }), + deepLinkId: 'slo', + }, + { + text: i18n.translate('xpack.slo.breadcrumbs.slosOutdatedDefinitions', { + defaultMessage: 'Outdated SLO Definitions', + }), + }, + ], + { serverless } + ); const [search, setSearch] = useState(''); const [activePage, setActivePage] = useState(0); diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_settings/slo_settings.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_settings/slo_settings.tsx index d2a2da4a5fafa..ca41c7561fb46 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_settings/slo_settings.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_settings/slo_settings.tsx @@ -16,17 +16,21 @@ import { HeaderMenu } from '../../components/header_menu/header_menu'; export function SloSettingsPage() { const { http: { basePath }, + serverless, } = useKibana().services; const { ObservabilityPageTemplate } = usePluginContext(); - useBreadcrumbs([ - { - href: basePath.prepend(paths.slosSettings), - text: i18n.translate('xpack.slo.breadcrumbs.slosSettingsText', { - defaultMessage: 'SLOs Settings', - }), - }, - ]); + useBreadcrumbs( + [ + { + href: basePath.prepend(paths.slosSettings), + text: i18n.translate('xpack.slo.breadcrumbs.slosSettingsText', { + defaultMessage: 'SLOs Settings', + }), + }, + ], + { serverless } + ); return ( 0 ? 2 : 1; + return ( { @@ -57,13 +63,14 @@ export function SloCardItemBadges({ slo, activeAlerts, rules, handleCreateRule } ) : ( <> + { if ((!isLoading && total === 0) || hasAtLeast('platinum') === false || isError) { diff --git a/x-pack/plugins/observability_solution/slo/server/assets/ingest_templates/slo_pipeline_template.ts b/x-pack/plugins/observability_solution/slo/server/assets/ingest_templates/slo_pipeline_template.ts index c378cd745397c..dc078f351eb3e 100644 --- a/x-pack/plugins/observability_solution/slo/server/assets/ingest_templates/slo_pipeline_template.ts +++ b/x-pack/plugins/observability_solution/slo/server/assets/ingest_templates/slo_pipeline_template.ts @@ -17,6 +17,12 @@ export const getSLOPipelineTemplate = (slo: SLODefinition) => ({ id: getSLOPipelineId(slo.id, slo.revision), description: `Ingest pipeline for SLO rollup data [id: ${slo.id}, revision: ${slo.revision}]`, processors: [ + { + set: { + field: '_id', + value: `{{{_id}}}-${slo.id}-${slo.revision}`, + }, + }, { set: { field: 'event.ingested', diff --git a/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/create_slo.test.ts.snap b/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/create_slo.test.ts.snap index d747d5083cd28..46acf96dde5c3 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/create_slo.test.ts.snap +++ b/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/create_slo.test.ts.snap @@ -12,6 +12,12 @@ Array [ "description": "Ingest pipeline for SLO rollup data [id: unique-id, revision: 1]", "id": ".slo-observability.sli.pipeline-unique-id-1", "processors": Array [ + Object { + "set": Object { + "field": "_id", + "value": "{{{_id}}}-unique-id-1", + }, + }, Object { "set": Object { "field": "event.ingested", diff --git a/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/reset_slo.test.ts.snap b/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/reset_slo.test.ts.snap index 00dc9bb4654ae..90690a4989586 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/reset_slo.test.ts.snap +++ b/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/reset_slo.test.ts.snap @@ -208,6 +208,12 @@ exports[`ResetSLO resets all associated resources 8`] = ` "description": "Ingest pipeline for SLO rollup data [id: irrelevant, revision: 1]", "id": ".slo-observability.sli.pipeline-irrelevant-1", "processors": Array [ + Object { + "set": Object { + "field": "_id", + "value": "{{{_id}}}-irrelevant-1", + }, + }, Object { "set": Object { "field": "event.ingested", diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/__snapshots__/synthetics_availability.test.ts.snap b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/__snapshots__/synthetics_availability.test.ts.snap new file mode 100644 index 0000000000000..3c71844678885 --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/__snapshots__/synthetics_availability.test.ts.snap @@ -0,0 +1,117 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Synthetics Availability Transform Generator returns the expected transform params 1`] = ` +Object { + "_meta": Object { + "managed": true, + "managed_by": "observability", + "version": 3.3, + }, + "defer_validation": true, + "description": "Rolled-up SLI data for SLO: irrelevant [id: irrelevant, revision: 1]", + "dest": Object { + "index": ".slo-observability.sli-v3.3", + "pipeline": ".slo-observability.sli.pipeline-irrelevant-1", + }, + "frequency": "1m", + "pivot": Object { + "aggregations": Object { + "slo.denominator": Object { + "filter": Object { + "term": Object { + "summary.final_attempt": true, + }, + }, + }, + "slo.numerator": Object { + "filter": Object { + "term": Object { + "monitor.status": "up", + }, + }, + }, + }, + "group_by": Object { + "@timestamp": Object { + "date_histogram": Object { + "field": "@timestamp", + "fixed_interval": "1m", + }, + }, + "monitor.config_id": Object { + "terms": Object { + "field": "config_id", + }, + }, + "monitor.name": Object { + "terms": Object { + "field": "monitor.name", + }, + }, + "observer.geo.name": Object { + "terms": Object { + "field": "observer.geo.name", + }, + }, + "observer.name": Object { + "terms": Object { + "field": "observer.name", + }, + }, + "slo.groupings.monitor.id": Object { + "terms": Object { + "field": "monitor.id", + }, + }, + "slo.groupings.monitor.name": Object { + "terms": Object { + "field": "monitor.name", + }, + }, + "slo.groupings.observer.geo.name": Object { + "terms": Object { + "field": "observer.geo.name", + }, + }, + }, + }, + "settings": Object { + "deduce_mappings": false, + "unattended": true, + }, + "source": Object { + "index": "synthetics-*", + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "summary.final_attempt": true, + }, + }, + Object { + "term": Object { + "meta.space_id": "custom-space", + }, + }, + Object { + "range": Object { + "@timestamp": Object { + "gte": "now-7d/d", + }, + }, + }, + ], + }, + }, + "runtime_mappings": Object {}, + }, + "sync": Object { + "time": Object { + "delay": "1m", + "field": "event.ingested", + }, + }, + "transform_id": "slo-irrelevant-1", +} +`; diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/__snapshots__/transform_generator.test.ts.snap b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/__snapshots__/transform_generator.test.ts.snap new file mode 100644 index 0000000000000..144a4fa35eda5 --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/__snapshots__/transform_generator.test.ts.snap @@ -0,0 +1,73 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Transform Generator builds common runtime mappings and group by with single group by 1`] = `Object {}`; + +exports[`Transform Generator builds common runtime mappings and group by with single group by 2`] = ` +Object { + "@timestamp": Object { + "date_histogram": Object { + "field": "@timestamp", + "fixed_interval": "1m", + }, + }, + "slo.groupings.example": Object { + "terms": Object { + "field": "example", + }, + }, +} +`; + +exports[`Transform Generator builds common runtime mappings and group by with single group by 3`] = `Object {}`; + +exports[`Transform Generator builds common runtime mappings and group by with single group by 4`] = ` +Object { + "@timestamp": Object { + "date_histogram": Object { + "field": "@timestamp", + "fixed_interval": "1m", + }, + }, + "slo.groupings.example": Object { + "terms": Object { + "field": "example", + }, + }, +} +`; + +exports[`Transform Generator builds common runtime mappings without multi group by 1`] = `Object {}`; + +exports[`Transform Generator builds common runtime mappings without multi group by 2`] = ` +Object { + "@timestamp": Object { + "date_histogram": Object { + "field": "@timestamp", + "fixed_interval": "1m", + }, + }, + "slo.groupings.example1": Object { + "terms": Object { + "field": "example1", + }, + }, + "slo.groupings.example2": Object { + "terms": Object { + "field": "example2", + }, + }, +} +`; + +exports[`Transform Generator builds empty runtime mappings without group by 1`] = `Object {}`; + +exports[`Transform Generator builds empty runtime mappings without group by 2`] = ` +Object { + "@timestamp": Object { + "date_histogram": Object { + "field": "@timestamp", + "fixed_interval": "1m", + }, + }, +} +`; diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.test.ts index f9caeb9f57c31..565a0d56d1ff4 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.test.ts @@ -5,13 +5,12 @@ * 2.0. */ -import { ALL_VALUE } from '@kbn/slo-schema'; import { dataViewsService } from '@kbn/data-views-plugin/server/mocks'; +import { ALL_VALUE } from '@kbn/slo-schema'; import { SLODefinition } from '../../domain/models'; +import { twoMinute } from '../fixtures/duration'; import { createSLO, createSyntheticsAvailabilityIndicator } from '../fixtures/slo'; import { SyntheticsAvailabilityTransformGenerator } from './synthetics_availability'; -import { SYNTHETICS_INDEX_PATTERN } from '../../../common/constants'; -import { twoMinute } from '../fixtures/duration'; const generator = new SyntheticsAvailabilityTransformGenerator(); @@ -22,119 +21,7 @@ describe('Synthetics Availability Transform Generator', () => { const slo = createSLO({ id: 'irrelevant', indicator: createSyntheticsAvailabilityIndicator() }); const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); - expect(transform).toEqual({ - _meta: { - managed: true, - managed_by: 'observability', - version: 3.3, - }, - defer_validation: true, - description: 'Rolled-up SLI data for SLO: irrelevant [id: irrelevant, revision: 1]', - dest: { - index: '.slo-observability.sli-v3.3', - pipeline: '.slo-observability.sli.pipeline-irrelevant-1', - }, - frequency: '1m', - pivot: { - aggregations: { - 'slo.denominator': { - filter: { - term: { - 'summary.final_attempt': true, - }, - }, - }, - 'slo.numerator': { - filter: { - term: { - 'monitor.status': 'up', - }, - }, - }, - }, - group_by: { - '@timestamp': { - date_histogram: { - field: '@timestamp', - fixed_interval: '1m', - }, - }, - 'monitor.config_id': { - terms: { - field: 'config_id', - }, - }, - 'monitor.name': { - terms: { - field: 'monitor.name', - }, - }, - 'observer.name': { - terms: { - field: 'observer.name', - }, - }, - 'observer.geo.name': { - terms: { - field: 'observer.geo.name', - }, - }, - 'slo.groupings.monitor.name': { - terms: { - field: 'monitor.name', - }, - }, - 'slo.groupings.observer.geo.name': { - terms: { - field: 'observer.geo.name', - }, - }, - 'slo.groupings.monitor.id': { - terms: { - field: 'monitor.id', - }, - }, - }, - }, - settings: { - deduce_mappings: false, - unattended: true, - }, - source: { - index: SYNTHETICS_INDEX_PATTERN, - query: { - bool: { - filter: [ - { - term: { - 'summary.final_attempt': true, - }, - }, - { - term: { - 'meta.space_id': 'custom-space', - }, - }, - { - range: { - '@timestamp': { - gte: 'now-7d/d', - }, - }, - }, - ], - }, - }, - runtime_mappings: {}, - }, - sync: { - time: { - delay: '1m', - field: 'event.ingested', - }, - }, - transform_id: 'slo-irrelevant-1', - }); + expect(transform).toMatchSnapshot(); expect(transform.source.query?.bool?.filter).toContainEqual({ term: { 'summary.final_attempt': true, diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.test.ts index ffb165fdb4326..9f07c6cfb5afa 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.test.ts @@ -17,19 +17,10 @@ describe('Transform Generator', () => { indicator: createAPMTransactionErrorRateIndicator(), }); const commonRuntime = generator.buildCommonRuntimeMappings(slo); - - expect(commonRuntime).toEqual({}); + expect(commonRuntime).toMatchSnapshot(); const commonGroupBy = generator.buildCommonGroupBy(slo); - - expect(commonGroupBy).toEqual({ - '@timestamp': { - date_histogram: { - field: '@timestamp', - fixed_interval: '1m', - }, - }, - }); + expect(commonGroupBy).toMatchSnapshot(); }); it.each(['example', ['example']])( @@ -42,24 +33,10 @@ describe('Transform Generator', () => { indicator, }); const commonRuntime = generator.buildCommonRuntimeMappings(slo); - - expect(commonRuntime).toEqual({}); + expect(commonRuntime).toMatchSnapshot(); const commonGroupBy = generator.buildCommonGroupBy(slo); - - expect(commonGroupBy).toEqual({ - '@timestamp': { - date_histogram: { - field: '@timestamp', - fixed_interval: '1m', - }, - }, - 'slo.groupings.example': { - terms: { - field: 'example', - }, - }, - }); + expect(commonGroupBy).toMatchSnapshot(); } ); @@ -71,28 +48,9 @@ describe('Transform Generator', () => { indicator, }); const commonRuntime = generator.buildCommonRuntimeMappings(slo); - - expect(commonRuntime).toEqual({}); + expect(commonRuntime).toMatchSnapshot(); const commonGroupBy = generator.buildCommonGroupBy(slo); - - expect(commonGroupBy).toEqual({ - '@timestamp': { - date_histogram: { - field: '@timestamp', - fixed_interval: '1m', - }, - }, - 'slo.groupings.example1': { - terms: { - field: 'example1', - }, - }, - 'slo.groupings.example2': { - terms: { - field: 'example2', - }, - }, - }); + expect(commonGroupBy).toMatchSnapshot(); }); }); diff --git a/x-pack/plugins/observability_solution/synthetics/common/field_names.ts b/x-pack/plugins/observability_solution/synthetics/common/field_names.ts index e7f8e83d73b4b..45be741982b01 100644 --- a/x-pack/plugins/observability_solution/synthetics/common/field_names.ts +++ b/x-pack/plugins/observability_solution/synthetics/common/field_names.ts @@ -15,6 +15,7 @@ export const OBSERVER_NAME = 'observer.name'; export const SERVICE_NAME = 'service.name'; export const OBSERVER_GEO_NAME = 'observer.geo.name'; export const ERROR_MESSAGE = 'error.message'; +export const ERROR_STACK_TRACE = 'error.stack_trace'; export const STATE_ID = 'monitor.state.id'; export const CERT_COMMON_NAME = 'tls.server.x509.subject.common_name'; diff --git a/x-pack/plugins/observability_solution/synthetics/common/requests/get_certs_request_body.ts b/x-pack/plugins/observability_solution/synthetics/common/requests/get_certs_request_body.ts index f151192a730ed..31f389a909004 100644 --- a/x-pack/plugins/observability_solution/synthetics/common/requests/get_certs_request_body.ts +++ b/x-pack/plugins/observability_solution/synthetics/common/requests/get_certs_request_body.ts @@ -150,7 +150,7 @@ export const getCertsRequestBody = ({ 'service', 'labels', 'tags', - 'error.message', + 'error', ], collapse: { field: 'tls.server.hash.sha256', @@ -222,6 +222,7 @@ export const processCertsResult = (result: CertificatesResults): CertResult => { locationId: ping?.observer?.name, locationName: ping?.observer?.geo?.name, errorMessage: ping?.error?.message, + errorStackTrace: ping?.error?.stack_trace, }; }); const total = result.aggregations?.total?.value ?? 0; diff --git a/x-pack/plugins/observability_solution/synthetics/common/rules/synthetics_rule_field_map.ts b/x-pack/plugins/observability_solution/synthetics/common/rules/synthetics_rule_field_map.ts index f82f44ba2d24d..390916026668c 100644 --- a/x-pack/plugins/observability_solution/synthetics/common/rules/synthetics_rule_field_map.ts +++ b/x-pack/plugins/observability_solution/synthetics/common/rules/synthetics_rule_field_map.ts @@ -32,6 +32,10 @@ export const syntheticsRuleFieldMap: FieldMap = { type: 'text', required: false, }, + 'error.stack_trace': { + type: 'wildcard', + required: false, + }, 'agent.name': { type: 'keyword', required: false, diff --git a/x-pack/plugins/observability_solution/synthetics/common/runtime_types/certs.ts b/x-pack/plugins/observability_solution/synthetics/common/runtime_types/certs.ts index 4fe14a54c0d66..49ac5573294e1 100644 --- a/x-pack/plugins/observability_solution/synthetics/common/runtime_types/certs.ts +++ b/x-pack/plugins/observability_solution/synthetics/common/runtime_types/certs.ts @@ -51,6 +51,7 @@ export const CertType = t.intersection([ '@timestamp': t.string, serviceName: t.string, errorMessage: t.string, + errorStackTrace: t.union([t.string, t.null]), labels: t.record(t.string, t.string), tags: t.array(t.string), }), diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/hooks/use_get_data_stream_statuses.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/hooks/use_get_data_stream_statuses.ts index f3b3136200bd7..00d301e9eb706 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/hooks/use_get_data_stream_statuses.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/hooks/use_get_data_stream_statuses.ts @@ -94,6 +94,7 @@ function toMissingDataStream({ privileges: { delete_index: true, manage_data_stream_lifecycle: true }, hidden: false, nextGenerationManagedBy: 'Data stream lifecycle', + indexMode: 'standard', }; } diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_settings_context.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_settings_context.tsx index 14799683d96d5..d380d95c90aa4 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_settings_context.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_settings_context.tsx @@ -38,7 +38,6 @@ export interface SyntheticsAppProps { setBadge: (badge?: ChromeBadge) => void; renderGlobalHelpControls(): void; commonlyUsedRanges: CommonlyUsedDateRange[]; - setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void; appMountParameters: AppMountParameters; isDev: boolean; isServerless: boolean; @@ -89,7 +88,6 @@ export const SyntheticsSettingsContextProvider: React.FC diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/hooks/use_breadcrumbs.test.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/hooks/use_breadcrumbs.test.tsx index 5e524eca31bda..c97cefc194069 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/hooks/use_breadcrumbs.test.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/hooks/use_breadcrumbs.test.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import { coreMock } from '@kbn/core/public/mocks'; import { ChromeBreadcrumb } from '@kbn/core/public'; import { render } from '../utils/testing'; import React from 'react'; @@ -18,6 +19,8 @@ import { } from '../utils/url_params/get_supported_url_params'; import { makeBaseBreadcrumb, useBreadcrumbs } from './use_breadcrumbs'; import { SyntheticsSettingsContext } from '../contexts'; +import { BehaviorSubject } from 'rxjs'; +import { ChromeStyle } from '@kbn/core-chrome-browser'; describe('useBreadcrumbs', () => { it('sets the given breadcrumbs', () => { @@ -71,9 +74,10 @@ describe('useBreadcrumbs', () => { const urlParams: SyntheticsUrlParams = getSupportedUrlParams({}); expect(JSON.stringify(getBreadcrumbs())).toEqual( JSON.stringify( - makeBaseBreadcrumb('/app/synthetics', '/app/observability', urlParams, false).concat( - expectedCrumbs - ) + [ + { text: 'Observability', href: '/app/observability/overview' }, + ...makeBaseBreadcrumb('/app/synthetics', urlParams), + ].concat(expectedCrumbs) ) ); }); @@ -84,6 +88,8 @@ const mockCore: () => [() => ChromeBreadcrumb[], any] = () => { const get = () => { return breadcrumbObj; }; + const defaultCoreMock = coreMock.createStart(); + const core = { application: { getUrlForApp: (app: string) => @@ -91,6 +97,8 @@ const mockCore: () => [() => ChromeBreadcrumb[], any] = () => { navigateToUrl: jest.fn(), }, chrome: { + ...defaultCoreMock.chrome, + getChromeStyle$: () => new BehaviorSubject('classic').asObservable(), setBreadcrumbs: (newBreadcrumbs: ChromeBreadcrumb[]) => { breadcrumbObj = newBreadcrumbs; }, diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/hooks/use_breadcrumbs.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/hooks/use_breadcrumbs.ts index 6d174f773e5a1..c311b08ff22f8 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/hooks/use_breadcrumbs.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/hooks/use_breadcrumbs.ts @@ -7,47 +7,20 @@ import { ChromeBreadcrumb } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; -import { MouseEvent, useContext, useEffect } from 'react'; +import { useMemo } from 'react'; import { EuiBreadcrumb } from '@elastic/eui'; import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { useBreadcrumbs as useObservabilityBreadcrumbs } from '@kbn/observability-shared-plugin/public'; +import { ClientPluginsStart } from '../../../plugin'; import { SyntheticsUrlParams, stringifyUrlParams } from '../utils/url_params'; import { useUrlParams } from './use_url_params'; import { PLUGIN } from '../../../../common/constants/plugin'; -import { SyntheticsSettingsContext } from '../contexts'; const EMPTY_QUERY = '?'; -function handleBreadcrumbClick( - breadcrumbs: ChromeBreadcrumb[], - navigateToHref?: (url: string) => Promise -) { - return breadcrumbs.map((bc) => ({ - ...bc, - ...(bc.href - ? { - onClick: (event: MouseEvent) => { - if (navigateToHref && bc.href) { - event.preventDefault(); - navigateToHref(bc.href); - } - }, - } - : {}), - ...(bc['data-test-subj'] - ? { - 'data-test-subj': bc['data-test-subj'], - } - : { - 'data-test-subj': bc.href, - }), - })); -} - export const makeBaseBreadcrumb = ( uptimePath: string, - observabilityPath: string, - params?: SyntheticsUrlParams, - isServerless?: boolean + params?: SyntheticsUrlParams ): EuiBreadcrumb[] => { if (params) { const crumbParams: Partial = { ...params }; @@ -59,18 +32,6 @@ export const makeBaseBreadcrumb = ( const baseBreadcrumbs: EuiBreadcrumb[] = []; - // serverless Kibana has a curated UX flow, and "Observability" is already a given, - // thus we don't need to include it explicitly in the breadcrumb trail - if (!isServerless) { - baseBreadcrumbs.push({ - text: i18n.translate('xpack.synthetics.breadcrumbs.observabilityText', { - defaultMessage: 'Observability', - }), - href: observabilityPath, - 'data-test-subj': 'observabilityPathBreadcrumb', - }); - } - baseBreadcrumbs.push({ text: i18n.translate('xpack.synthetics.breadcrumbs.overviewBreadcrumbText', { defaultMessage: 'Synthetics', @@ -84,32 +45,12 @@ export const makeBaseBreadcrumb = ( export const useBreadcrumbs = (extraCrumbs: ChromeBreadcrumb[]) => { const params = useUrlParams()[0](); - const kibana = useKibana(); - const { setBreadcrumbs, isServerless } = useContext(SyntheticsSettingsContext); + const kibana = useKibana(); const syntheticsPath = kibana.services.application?.getUrlForApp(PLUGIN.SYNTHETICS_PLUGIN_ID) ?? ''; - const observabilityPath = - kibana.services.application?.getUrlForApp('observability-overview') ?? ''; - const navigate = kibana.services.application?.navigateToUrl; + const breadcrumbs = useMemo(() => { + return makeBaseBreadcrumb(syntheticsPath, params).concat(extraCrumbs); + }, [extraCrumbs, params, syntheticsPath]); - useEffect(() => { - if (setBreadcrumbs) { - setBreadcrumbs( - handleBreadcrumbClick( - makeBaseBreadcrumb(syntheticsPath, observabilityPath, params, isServerless).concat( - extraCrumbs - ), - navigate - ) - ); - } - }, [ - syntheticsPath, - observabilityPath, - extraCrumbs, - navigate, - params, - setBreadcrumbs, - isServerless, - ]); + useObservabilityBreadcrumbs(breadcrumbs, { serverless: kibana.services.serverless }); }; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/render_app.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/render_app.tsx index 925f39fca7c07..19f97a6e50960 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/render_app.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/render_app.tsx @@ -66,7 +66,6 @@ export const getSyntheticsAppProps = (): SyntheticsAppProps => { setBadge, appMountParameters, isServerless, - setBreadcrumbs: startPlugins.serverless?.setBreadcrumbs ?? coreStart.chrome.setBreadcrumbs, }; }; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/testing/rtl_helpers.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/testing/rtl_helpers.tsx index af007700c4484..93ff3de42d360 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/testing/rtl_helpers.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/testing/rtl_helpers.tsx @@ -7,7 +7,7 @@ import React, { ReactElement, ReactNode } from 'react'; import { i18n } from '@kbn/i18n'; -import { of } from 'rxjs'; +import { BehaviorSubject, of } from 'rxjs'; // eslint-disable-next-line import/no-extraneous-dependencies import { render as reactTestLibRender, @@ -29,6 +29,7 @@ import { KibanaContextProvider, KibanaServices } from '@kbn/kibana-react-plugin/ import { triggersActionsUiMock } from '@kbn/triggers-actions-ui-plugin/public/mocks'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; +import { ChromeStyle } from '@kbn/core-chrome-browser'; import { mockState } from './__mocks__/synthetics_store.mock'; import { MountWithReduxProvider } from './helper_with_redux'; import { AppState } from '../../state'; @@ -166,6 +167,10 @@ export const mockCore: () => Partial = () => {
), }, + chrome: { + ...defaultCore.chrome, + getChromeStyle$: () => new BehaviorSubject('classic').asObservable(), + }, }; return core; diff --git a/x-pack/plugins/observability_solution/synthetics/server/alert_rules/status_rule/message_utils.ts b/x-pack/plugins/observability_solution/synthetics/server/alert_rules/status_rule/message_utils.ts index 812a900667cf7..6f01e9b234bf6 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/alert_rules/status_rule/message_utils.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/alert_rules/status_rule/message_utils.ts @@ -24,6 +24,7 @@ import { AGENT_NAME, STATE_ID, SERVICE_NAME, + ERROR_STACK_TRACE, } from '../../../common/field_names'; import { OverviewPing } from '../../../common/runtime_types'; import { UNNAMED_LOCATION } from '../../../common/constants'; @@ -42,6 +43,8 @@ export const getMonitorAlertDocument = ( [OBSERVER_GEO_NAME]: locationNames, [OBSERVER_NAME]: locationIds, [ERROR_MESSAGE]: monitorSummary.lastErrorMessage, + // done to avoid assigning null to the field + [ERROR_STACK_TRACE]: monitorSummary.lastErrorStack ? monitorSummary.lastErrorStack : undefined, [AGENT_NAME]: monitorSummary.hostName, [ALERT_REASON]: monitorSummary.reason, [STATE_ID]: monitorSummary.stateId, @@ -114,7 +117,9 @@ export const getMonitorSummary = ({ monitorId: monitorInfo.monitor?.id, monitorName, monitorType: typeToLabelMap[monitorInfo.monitor?.type] || monitorInfo.monitor?.type, - lastErrorMessage: monitorInfo.error?.message!, + lastErrorMessage: monitorInfo.error?.message, + // done to avoid assigning null to the field + lastErrorStack: monitorInfo.error?.stack_trace ? monitorInfo.error?.stack_trace : undefined, serviceName: monitorInfo.service?.name, labels: monitorInfo.labels, locationName: formattedLocationName, diff --git a/x-pack/plugins/observability_solution/synthetics/server/alert_rules/status_rule/types.ts b/x-pack/plugins/observability_solution/synthetics/server/alert_rules/status_rule/types.ts index 82294e55c08fc..85ae989876107 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/alert_rules/status_rule/types.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/alert_rules/status_rule/types.ts @@ -69,6 +69,7 @@ export interface MonitorSummaryStatusRule { }; stateId?: string; lastErrorMessage?: string; + lastErrorStack?: string | null; timestamp: string; labels?: Record; } diff --git a/x-pack/plugins/observability_solution/synthetics/server/alert_rules/tls_rule/message_utils.ts b/x-pack/plugins/observability_solution/synthetics/server/alert_rules/tls_rule/message_utils.ts index 15a6f093becd9..a6a7d82fb3335 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/alert_rules/tls_rule/message_utils.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/alert_rules/tls_rule/message_utils.ts @@ -29,6 +29,7 @@ import { CERT_VALID_NOT_AFTER, CERT_VALID_NOT_BEFORE, ERROR_MESSAGE, + ERROR_STACK_TRACE, MONITOR_ID, MONITOR_NAME, MONITOR_TYPE, @@ -103,6 +104,7 @@ export const getCertSummary = (cert: Cert, expirationThreshold: number, ageThres configId: cert.configId, monitorTags: cert.tags, errorMessage: cert.errorMessage, + errorStackTrace: cert.errorStackTrace, labels: cert.labels, }; }; @@ -123,6 +125,8 @@ export const getTLSAlertDocument = (cert: Cert, monitorSummary: CertSummary, uui [OBSERVER_GEO_NAME]: monitorSummary.locationName ? [monitorSummary.locationName] : [], [OBSERVER_NAME]: monitorSummary.locationId ? [monitorSummary.locationId] : [], [ERROR_MESSAGE]: monitorSummary.errorMessage, + // done to avoid assigning null to the field + [ERROR_STACK_TRACE]: monitorSummary.errorStackTrace ? monitorSummary.errorStackTrace : undefined, 'location.id': monitorSummary.locationId ? [monitorSummary.locationId] : [], 'location.name': monitorSummary.locationName ? [monitorSummary.locationName] : [], labels: cert.labels, diff --git a/x-pack/plugins/observability_solution/synthetics/server/alert_rules/translations.ts b/x-pack/plugins/observability_solution/synthetics/server/alert_rules/translations.ts index 40017b00646f1..03063f92ee56c 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/alert_rules/translations.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/alert_rules/translations.ts @@ -79,6 +79,15 @@ export const commonMonitorStateI18: Array<{ } ), }, + { + name: 'lastErrorStack', + description: i18n.translate( + 'xpack.synthetics.alertRules.monitorStatus.actionVariables.state.lastErrorStack', + { + defaultMessage: 'Monitor last error stack trace.', + } + ), + }, { name: 'locationName', description: i18n.translate( diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/formatters/saved_object_to_monitor.test.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/formatters/saved_object_to_monitor.test.ts index 40aec3b468a37..18c4bf71cdfec 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/formatters/saved_object_to_monitor.test.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/formatters/saved_object_to_monitor.test.ts @@ -70,6 +70,9 @@ describe('mergeSourceMonitor', () => { const result = mapSavedObjectToMonitor({ monitor: { attributes: testMonitor } } as any); expect(result).toEqual({ + __ui: { + is_tls_enabled: false, + }, alert: { status: { enabled: true, @@ -78,53 +81,48 @@ describe('mergeSourceMonitor', () => { enabled: true, }, }, + 'check.request.method': 'GET', + 'check.response.status': ['404'], config_id: 'ae88f0aa-9c7d-4a5f-96dc-89d65a0ca947', custom_heartbeat_id: 'todos-lightweight-test-projects-default', enabled: true, id: 'todos-lightweight-test-projects-default', ipv4: true, ipv6: true, - locations: ['us_central', 'us_east'], - private_locations: ['pvt_us_east'], - max_redirects: 0, + locations: [ + { + geo: { + lat: 41.25, + lon: -95.86, + }, + id: 'us_central', + isServiceManaged: true, + label: 'North America - US Central', + }, + ], + max_attempts: 2, + max_redirects: '0', mode: 'any', name: 'Todos Lightweight', namespace: 'default', + origin: 'project', original_space: 'default', - proxy_url: '', + project_id: 'test-projects', + 'response.include_body': 'on_error', + 'response.include_body_max_bytes': '1024', + 'response.include_headers': true, retest_on_failure: true, revision: 21, schedule: { number: '3', unit: 'm', }, - 'service.name': '', - tags: [], + 'ssl.key': 'test-key', + 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], + 'ssl.verification_mode': 'full', timeout: '16', type: 'http', url: '${devUrl}', - 'url.port': null, - ssl: { - certificate: '', - certificate_authorities: '', - supported_protocols: ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], - verification_mode: 'full', - key: 'test-key', - }, - response: { - include_body: 'on_error', - include_body_max_bytes: '1024', - include_headers: true, - }, - check: { - request: { - method: 'GET', - }, - response: { - status: ['404'], - }, - }, - params: {}, }); }); @@ -157,30 +155,12 @@ describe('mergeSourceMonitor', () => { locations: [ { geo: { - lon: -95.86, lat: 41.25, - }, - isServiceManaged: true, - id: 'us_central', - label: 'North America - US Central', - }, - { - geo: { lon: -95.86, - lat: 41.25, }, + id: 'us_central', isServiceManaged: true, - id: 'us-east4-a', - label: 'US East', - }, - { - geo: { - lon: -95.86, - lat: 41.25, - }, - isServiceManaged: false, - id: 'pvt_us_east', - label: 'US East (Private)', + label: 'North America - US Central', }, ], max_redirects: '0', @@ -249,24 +229,6 @@ const testMonitor = { id: 'us_central', label: 'North America - US Central', }, - { - geo: { - lon: -95.86, - lat: 41.25, - }, - isServiceManaged: true, - id: 'us-east4-a', - label: 'US East', - }, - { - geo: { - lon: -95.86, - lat: 41.25, - }, - isServiceManaged: false, - id: 'pvt_us_east', - label: 'US East (Private)', - }, ], namespace: 'default', origin: 'project', diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/formatters/saved_object_to_monitor.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/formatters/saved_object_to_monitor.ts index 06746fc235769..4156620abdd78 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/formatters/saved_object_to_monitor.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/formatters/saved_object_to_monitor.ts @@ -7,7 +7,6 @@ import { SavedObject } from '@kbn/core/server'; import { mergeWith, omit, omitBy } from 'lodash'; -import { LocationsMap } from '../../../synthetics_service/project_monitor/normalizers/common_fields'; import { ConfigKey, EncryptedSyntheticsMonitor, @@ -22,14 +21,6 @@ const keysToOmit = [ ConfigKey.CONFIG_HASH, ConfigKey.JOURNEY_ID, ConfigKey.FORM_MONITOR_TYPE, - ConfigKey.MAX_ATTEMPTS, - ConfigKey.MONITOR_SOURCE_TYPE, - ConfigKey.METADATA, - ConfigKey.SOURCE_PROJECT_CONTENT, - ConfigKey.PROJECT_ID, - ConfigKey.JOURNEY_FILTERS_MATCH, - ConfigKey.JOURNEY_FILTERS_TAGS, - ConfigKey.MONITOR_SOURCE_TYPE, ]; type Result = MonitorFieldsResult & { @@ -39,14 +30,25 @@ type Result = MonitorFieldsResult & { ssl: Record; response: Record; check: Record; - locations: string[]; - private_locations: string[]; }; export const transformPublicKeys = (result: Result) => { + if (result[ConfigKey.SOURCE_INLINE]) { + result.inline_script = result[ConfigKey.SOURCE_INLINE]; + } + if (result[ConfigKey.HOSTS]) { + result.host = result[ConfigKey.HOSTS]; + } + if (result[ConfigKey.PARAMS]) { + try { + result[ConfigKey.PARAMS] = JSON.parse(result[ConfigKey.PARAMS] ?? '{}'); + } catch (e) { + // ignore + } + } + let formattedResult = { ...result, - ...formatLocations(result), [ConfigKey.PARAMS]: formatParams(result), retest_on_failure: (result[ConfigKey.MAX_ATTEMPTS] ?? 1) > 1, ...(result[ConfigKey.HOSTS] && { host: result[ConfigKey.HOSTS] }), @@ -58,20 +60,8 @@ export const transformPublicKeys = (result: Result) => { ...(result[ConfigKey.SOURCE_INLINE] && { inline_script: result[ConfigKey.SOURCE_INLINE] }), [ConfigKey.PLAYWRIGHT_OPTIONS]: formatPWOptions(result), }; - } else { - formattedResult.ssl = formatNestedFields(formattedResult, 'ssl'); - formattedResult.response = formatNestedFields(formattedResult, 'response'); - formattedResult.check = formatNestedFields(formattedResult, 'check'); - if (formattedResult[ConfigKey.MAX_REDIRECTS]) { - formattedResult[ConfigKey.MAX_REDIRECTS] = Number(formattedResult[ConfigKey.MAX_REDIRECTS]); - } } - const res = omit(formattedResult, keysToOmit) as Result; - - return omitBy( - res, - (_, key) => key.startsWith('response.') || key.startsWith('ssl.') || key.startsWith('check.') - ); + return omit(formattedResult, keysToOmit) as Result; }; export function mapSavedObjectToMonitor({ @@ -81,7 +71,7 @@ export function mapSavedObjectToMonitor({ monitor: SavedObject; internal?: boolean; }) { - const result = { + let result = { ...monitor.attributes, created_at: monitor.created_at, updated_at: monitor.updated_at, @@ -89,7 +79,9 @@ export function mapSavedObjectToMonitor({ if (internal) { return result; } - return transformPublicKeys(result); + result = transformPublicKeys(result); + // omit undefined value or null value + return omitBy(result, removeMonitorEmptyValues); } export function mergeSourceMonitor( normalizedPreviousMonitor: EncryptedSyntheticsMonitor, @@ -108,24 +100,6 @@ const customizer = (destVal: any, srcValue: any, key: string) => { } }; -const formatLocations = (config: MonitorFields) => { - const locMap = Object.entries(LocationsMap); - const locations = config[ConfigKey.LOCATIONS] - ?.filter((location) => location.isServiceManaged) - .map((location) => { - return locMap.find(([_key, value]) => value === location.id)?.[0] ?? location.id; - }); - - const privateLocations = config[ConfigKey.LOCATIONS] - ?.filter((location) => !location.isServiceManaged) - .map((location) => location.id); - - return { - ...(locations && { locations }), - ...(privateLocations && { private_locations: privateLocations }), - }; -}; - const formatParams = (config: MonitorFields) => { if (config[ConfigKey.PARAMS]) { try { @@ -177,3 +151,17 @@ const formatNestedFields = ( return obj; }; + +export const removeMonitorEmptyValues = (v: any) => { + // value is falsy + return ( + v === undefined || + v === null || + // value is empty string + (typeof v === 'string' && v.trim() === '') || + // is empty array + (Array.isArray(v) && v.length === 0) || + // object is has no values + (typeof v === 'object' && Object.keys(v).length === 0) + ); +}; diff --git a/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/private_location/synthetics_private_location.ts b/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/private_location/synthetics_private_location.ts index fe5f74529121e..9ed34399e74f2 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/private_location/synthetics_private_location.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/private_location/synthetics_private_location.ts @@ -369,7 +369,10 @@ export class SyntheticsPrivateLocation { return await this.server.fleet.packagePolicyService.bulkCreate( soClient, esClient, - newPolicies + newPolicies, + { + asyncDeploy: true, + } ); } } @@ -384,6 +387,7 @@ export class SyntheticsPrivateLocation { policiesToUpdate, { force: true, + asyncDeploy: true, } ); return failedPolicies; @@ -401,6 +405,7 @@ export class SyntheticsPrivateLocation { policyIdsToDelete, { force: true, + asyncDeploy: true, } ); } catch (e) { @@ -430,6 +435,7 @@ export class SyntheticsPrivateLocation { policyIdsToDelete, { force: true, + asyncDeploy: true, } ); const failedPolicies = result?.filter((policy) => { diff --git a/x-pack/plugins/observability_solution/synthetics/tsconfig.json b/x-pack/plugins/observability_solution/synthetics/tsconfig.json index 24411ebdcb0c5..5df6d4257b4e9 100644 --- a/x-pack/plugins/observability_solution/synthetics/tsconfig.json +++ b/x-pack/plugins/observability_solution/synthetics/tsconfig.json @@ -104,7 +104,8 @@ "@kbn/babel-register", "@kbn/slo-plugin", "@kbn/ebt-tools", - "@kbn/alerting-types" + "@kbn/alerting-types", + "@kbn/core-chrome-browser" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/observability_solution/uptime/common/rules/uptime_rule_field_map.ts b/x-pack/plugins/observability_solution/uptime/common/rules/uptime_rule_field_map.ts index 6e0f73e183462..c157177b585ba 100644 --- a/x-pack/plugins/observability_solution/uptime/common/rules/uptime_rule_field_map.ts +++ b/x-pack/plugins/observability_solution/uptime/common/rules/uptime_rule_field_map.ts @@ -32,6 +32,10 @@ export const uptimeRuleFieldMap: FieldMap = { type: 'text', required: false, }, + 'error.stack_trace': { + type: 'wildcard', + required: false, + }, 'agent.name': { type: 'keyword', required: false, diff --git a/x-pack/plugins/observability_solution/ux/public/application/application.test.tsx b/x-pack/plugins/observability_solution/ux/public/application/application.test.tsx index ef9c7df0a1a8d..2b9dd676eac17 100644 --- a/x-pack/plugins/observability_solution/ux/public/application/application.test.tsx +++ b/x-pack/plugins/observability_solution/ux/public/application/application.test.tsx @@ -17,6 +17,8 @@ import { coreMock } from '@kbn/core/public/mocks'; import { merge } from 'lodash'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; +import { BehaviorSubject } from 'rxjs'; +import { ChromeStyle } from '@kbn/core-chrome-browser'; jest.mock('../services/rest/data_view', () => ({ createStaticDataView: () => Promise.resolve(undefined), @@ -122,6 +124,10 @@ const mockCore = merge({}, coreStart, { return uiSettings[key]; }, }, + chrome: { + ...coreStart.chrome, + getChromeStyle$: () => new BehaviorSubject('classic').asObservable(), + }, }); export const mockApmPluginContextValue = { diff --git a/x-pack/plugins/observability_solution/ux/tsconfig.json b/x-pack/plugins/observability_solution/ux/tsconfig.json index 94da70641f150..b27a700aa9b1f 100644 --- a/x-pack/plugins/observability_solution/ux/tsconfig.json +++ b/x-pack/plugins/observability_solution/ux/tsconfig.json @@ -49,7 +49,8 @@ "@kbn/react-kibana-context-render", "@kbn/react-kibana-context-theme", "@kbn/search-types", - "@kbn/server-route-repository-utils" + "@kbn/server-route-repository-utils", + "@kbn/core-chrome-browser" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/osquery/cypress/e2e/all/alerts_automated_action_results.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/alerts_automated_action_results.cy.ts index cd36950ba3b60..a815497e40c96 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/alerts_automated_action_results.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/alerts_automated_action_results.cy.ts @@ -13,6 +13,8 @@ const UUID_REGEX = '[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}- // FLAKY: https://github.com/elastic/kibana/issues/178404 // FLAKY: https://github.com/elastic/kibana/issues/197335 +// Failing: See https://github.com/elastic/kibana/issues/197335 +// Failing: See https://github.com/elastic/kibana/issues/178404 describe.skip('Alert Flyout Automated Action Results', () => { let ruleId: string; diff --git a/x-pack/plugins/osquery/cypress/e2e/roles/alert_test.cy.ts b/x-pack/plugins/osquery/cypress/e2e/roles/alert_test.cy.ts index b332951b1a444..2eaf015f23220 100644 --- a/x-pack/plugins/osquery/cypress/e2e/roles/alert_test.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/roles/alert_test.cy.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { waitForAlertsToPopulate } from '@kbn/test-suites-xpack/security_solution_cypress/cypress/tasks/create_new_rule'; import { disableNewFeaturesTours } from '../../tasks/navigation'; import { initializeDataViews } from '../../tasks/login'; import { checkResults, clickRuleName, submitQuery } from '../../tasks/live_query'; @@ -31,9 +32,8 @@ describe('Alert Test', { tags: ['@ess'] }, () => { onBeforeLoad: (win) => disableNewFeaturesTours(win), }); clickRuleName(ruleName); - cy.getBySel('expand-event').first().click({ force: true }); - - cy.wait(500); + waitForAlertsToPopulate(); + cy.getBySel('expand-event').first().click(); cy.getBySel('securitySolutionFlyoutInvestigationGuideButton').click(); cy.contains('Get processes').click(); }); diff --git a/x-pack/plugins/osquery/cypress/tasks/navigation.ts b/x-pack/plugins/osquery/cypress/tasks/navigation.ts index 72107207d7a8c..a0747706ffc15 100644 --- a/x-pack/plugins/osquery/cypress/tasks/navigation.ts +++ b/x-pack/plugins/osquery/cypress/tasks/navigation.ts @@ -45,8 +45,6 @@ export enum NAV_SEARCH_INPUT_OSQUERY_RESULTS { export const NEW_FEATURES_TOUR_STORAGE_KEYS = { RULE_MANAGEMENT_PAGE: 'securitySolution.rulesManagementPage.newFeaturesTour.v8.13', TIMELINES: 'securitySolution.security.timelineFlyoutHeader.saveTimelineTour', - TIMELINE: 'securitySolution.timeline.newFeaturesTour.v8.12', - FLYOUT: 'securitySolution.documentDetails.newFeaturesTour.v8.14', KNOWLEDGE_BASE: 'elasticAssistant.knowledgeBase.newFeaturesTour.v8.16', }; diff --git a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policies.ts b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policies.ts index 9e84410712506..64b10f0a8248e 100644 --- a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policies.ts +++ b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policies.ts @@ -59,7 +59,7 @@ export const getAgentPoliciesRoute = (router: IRouter, osqueryContext: OsqueryAp (agentPolicy: GetAgentPoliciesResponseItem) => agentService?.asInternalUser .getAgentStatusForAgentPolicy(agentPolicy.id) - .then(({ total: agentTotal }) => (agentPolicy.agents = agentTotal)), + .then(({ active: agentTotal }) => (agentPolicy.agents = agentTotal)), { concurrency: 10 } ); } diff --git a/x-pack/plugins/rule_registry/server/index.ts b/x-pack/plugins/rule_registry/server/index.ts index 826d0d6f23bab..de0685b8c9617 100644 --- a/x-pack/plugins/rule_registry/server/index.ts +++ b/x-pack/plugins/rule_registry/server/index.ts @@ -25,13 +25,6 @@ export * from './rule_data_plugin_service'; export * from './rule_data_client'; export * from './alert_data_client/audit_events'; -export { createLifecycleRuleTypeFactory } from './utils/create_lifecycle_rule_type_factory'; -export type { - LifecycleRuleExecutor, - LifecycleAlertService, - LifecycleAlertServices, -} from './utils/create_lifecycle_executor'; -export { createLifecycleExecutor } from './utils/create_lifecycle_executor'; export { createPersistenceRuleTypeWrapper } from './utils/create_persistence_rule_type_wrapper'; export * from './utils/persistence_types'; export type { AlertsClient } from './alert_data_client/alerts_client'; diff --git a/x-pack/plugins/rule_registry/server/mocks.ts b/x-pack/plugins/rule_registry/server/mocks.ts index 7ab1391ca1dec..ef5ae00ca0c56 100644 --- a/x-pack/plugins/rule_registry/server/mocks.ts +++ b/x-pack/plugins/rule_registry/server/mocks.ts @@ -11,10 +11,8 @@ import { ruleDataServiceMock, RuleDataServiceMock, } from './rule_data_plugin_service/rule_data_plugin_service.mock'; -import { createLifecycleAlertServicesMock } from './utils/lifecycle_alert_services.mock'; export const ruleRegistryMocks = { - createLifecycleAlertServices: createLifecycleAlertServicesMock, createRuleDataService: ruleDataServiceMock.create, createRuleDataClient: createRuleDataClientMock, createAlertsClientMock: alertsClientMock, diff --git a/x-pack/plugins/rule_registry/server/plugin.ts b/x-pack/plugins/rule_registry/server/plugin.ts index 7f6b6e0bf6002..60ee2256ae377 100644 --- a/x-pack/plugins/rule_registry/server/plugin.ts +++ b/x-pack/plugins/rule_registry/server/plugin.ts @@ -29,7 +29,6 @@ import type { PluginSetup as DataPluginSetup, } from '@kbn/data-plugin/server'; -import { createLifecycleRuleTypeFactory } from './utils/create_lifecycle_rule_type_factory'; import type { RuleRegistryPluginConfig } from './config'; import { type IRuleDataService, RuleDataService, Dataset } from './rule_data_plugin_service'; import { AlertsClientFactory } from './alert_data_client/alerts_client_factory'; @@ -52,7 +51,6 @@ export interface RuleRegistryPluginStartDependencies { export interface RuleRegistryPluginSetupContract { ruleDataService: IRuleDataService; - createLifecycleRuleTypeFactory: typeof createLifecycleRuleTypeFactory; dataset: typeof Dataset; } @@ -153,7 +151,6 @@ export class RuleRegistryPlugin return { ruleDataService: this.ruleDataService, - createLifecycleRuleTypeFactory, dataset: Dataset, }; } diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts deleted file mode 100644 index b895c49c14a5f..0000000000000 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts +++ /dev/null @@ -1,2408 +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 { loggerMock } from '@kbn/logging-mocks'; -import { pick } from 'lodash'; -import { - ALERT_INSTANCE_ID, - ALERT_MAINTENANCE_WINDOW_IDS, - ALERT_RULE_CATEGORY, - ALERT_RULE_CONSUMER, - ALERT_RULE_NAME, - ALERT_RULE_PRODUCER, - ALERT_RULE_TYPE_ID, - ALERT_RULE_UUID, - ALERT_STATUS, - ALERT_STATUS_ACTIVE, - ALERT_STATUS_RECOVERED, - ALERT_WORKFLOW_STATUS, - ALERT_UUID, - EVENT_ACTION, - EVENT_KIND, - SPACE_IDS, - ALERT_FLAPPING, - TAGS, - ALERT_CONSECUTIVE_MATCHES, -} from '../../common/technical_rule_data_field_names'; -import { createRuleDataClientMock } from '../rule_data_client/rule_data_client.mock'; -import { createLifecycleExecutor } from './create_lifecycle_executor'; -import { createDefaultAlertExecutorOptions } from './rule_executor.test_helpers'; - -describe('createLifecycleExecutor', () => { - it('wraps and unwraps the original executor state', async () => { - const logger = loggerMock.create(); - const ruleDataClientMock = createRuleDataClientMock(); - // @ts-ignore 4.3.5 upgrade - Expression produces a union type that is too complex to represent.ts(2590) - const executor = createLifecycleExecutor( - logger, - ruleDataClientMock - )<{}, TestRuleState, never, never, never>(async (options) => { - expect(options.state).toEqual(initialRuleState); - - const nextRuleState: TestRuleState = { - aRuleStateKey: 'NEXT_RULE_STATE_VALUE', - }; - - return { state: nextRuleState }; - }); - - const newExecutorResult = await executor( - createDefaultAlertExecutorOptions({ - params: {}, - state: { wrapped: initialRuleState, trackedAlerts: {}, trackedAlertsRecovered: {} }, - logger, - }) - ); - - expect(newExecutorResult.state).toEqual({ - wrapped: { - aRuleStateKey: 'NEXT_RULE_STATE_VALUE', - }, - trackedAlerts: {}, - trackedAlertsRecovered: {}, - }); - }); - - it('writes initial documents for newly firing alerts', async () => { - const logger = loggerMock.create(); - const ruleDataClientMock = createRuleDataClientMock(); - const executor = createLifecycleExecutor( - logger, - ruleDataClientMock - )<{}, TestRuleState, never, never, never>(async ({ services, state }) => { - services.alertWithLifecycle({ - id: 'TEST_ALERT_0', - fields: { [TAGS]: ['source-tag1', 'source-tag2'] }, - }); - services.alertWithLifecycle({ - id: 'TEST_ALERT_1', - fields: { [TAGS]: ['source-tag3', 'source-tag4'] }, - }); - - return { state }; - }); - - await executor( - createDefaultAlertExecutorOptions({ - params: {}, - state: { wrapped: initialRuleState, trackedAlerts: {}, trackedAlertsRecovered: {} }, - logger, - }) - ); - - expect((await ruleDataClientMock.getWriter()).bulk).toHaveBeenCalledWith( - expect.objectContaining({ - body: [ - // alert documents - { create: { _id: expect.any(String) } }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [EVENT_ACTION]: 'open', - [EVENT_KIND]: 'signal', - [TAGS]: ['source-tag1', 'source-tag2', 'rule-tag1', 'rule-tag2'], - }), - { create: { _id: expect.any(String) } }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [EVENT_ACTION]: 'open', - [EVENT_KIND]: 'signal', - [TAGS]: ['source-tag3', 'source-tag4', 'rule-tag1', 'rule-tag2'], - }), - ], - }) - ); - expect((await ruleDataClientMock.getWriter()).bulk).not.toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.arrayContaining([ - // evaluation documents - { create: {} }, - expect.objectContaining({ - [EVENT_KIND]: 'event', - }), - ]), - }) - ); - }); - - it('updates existing documents for repeatedly firing alerts', async () => { - const logger = loggerMock.create(); - const ruleDataClientMock = createRuleDataClientMock(); - ruleDataClientMock.getReader().search.mockResolvedValue({ - hits: { - hits: [ - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_UUID]: 'ALERT_0_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_WORKFLOW_STATUS]: 'closed', - [SPACE_IDS]: ['fake-space-id'], - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must show up in the written doc - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_UUID]: 'ALERT_1_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_WORKFLOW_STATUS]: 'open', - [SPACE_IDS]: ['fake-space-id'], - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must not show up in the written doc - }, - _index: '.alerts-index-name', - _seq_no: 1, - _primary_term: 3, - }, - ], - }, - } as any); - const executor = createLifecycleExecutor( - logger, - ruleDataClientMock - )<{}, TestRuleState, never, never, never>(async ({ services, state }) => { - services.alertWithLifecycle({ - id: 'TEST_ALERT_0', - fields: {}, - }); - services.alertWithLifecycle({ - id: 'TEST_ALERT_1', - fields: {}, - }); - - return { state }; - }); - - await executor( - createDefaultAlertExecutorOptions({ - alertId: 'TEST_ALERT_0', - params: {}, - state: { - wrapped: initialRuleState, - trackedAlerts: { - TEST_ALERT_0: { - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_1: { - alertId: 'TEST_ALERT_1', - alertUuid: 'TEST_ALERT_1_UUID', - started: '2020-01-02T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - }, - trackedAlertsRecovered: {}, - }, - logger, - }) - ); - - expect((await ruleDataClientMock.getWriter()).bulk).toHaveBeenCalledWith( - expect.objectContaining({ - body: [ - // alert document - { - index: { - _id: 'TEST_ALERT_0_UUID', - _index: '.alerts-index-name', - if_primary_term: 2, - if_seq_no: 4, - require_alias: false, - }, - }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_WORKFLOW_STATUS]: 'closed', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, - - [EVENT_ACTION]: 'active', - [EVENT_KIND]: 'signal', - }), - { - index: { - _id: 'TEST_ALERT_1_UUID', - _index: '.alerts-index-name', - if_primary_term: 3, - if_seq_no: 1, - require_alias: false, - }, - }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_WORKFLOW_STATUS]: 'open', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - - [EVENT_ACTION]: 'active', - [EVENT_KIND]: 'signal', - }), - ], - }) - ); - expect((await ruleDataClientMock.getWriter()).bulk).not.toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.arrayContaining([ - // evaluation documents - { index: {} }, - expect.objectContaining({ - [EVENT_KIND]: 'event', - }), - ]), - }) - ); - }); - - it('logs warning if existing documents are in unexpected index', async () => { - const logger = loggerMock.create(); - const ruleDataClientMock = createRuleDataClientMock(); - ruleDataClientMock.getReader().search.mockResolvedValue({ - hits: { - hits: [ - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_UUID]: 'ALERT_0_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_WORKFLOW_STATUS]: 'closed', - [SPACE_IDS]: ['fake-space-id'], - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must show up in the written doc - }, - _index: 'partial-.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_UUID]: 'ALERT_1_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_WORKFLOW_STATUS]: 'open', - [SPACE_IDS]: ['fake-space-id'], - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must not show up in the written doc - }, - _index: '.alerts-index-name', - _seq_no: 1, - _primary_term: 3, - }, - ], - }, - } as any); - const executor = createLifecycleExecutor( - logger, - ruleDataClientMock - )<{}, TestRuleState, never, never, never>(async ({ services, state }) => { - services.alertWithLifecycle({ - id: 'TEST_ALERT_0', - fields: {}, - }); - services.alertWithLifecycle({ - id: 'TEST_ALERT_1', - fields: {}, - }); - - return { state }; - }); - - await executor( - createDefaultAlertExecutorOptions({ - alertId: 'TEST_ALERT_0', - params: {}, - state: { - wrapped: initialRuleState, - trackedAlerts: { - TEST_ALERT_0: { - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_1: { - alertId: 'TEST_ALERT_1', - alertUuid: 'TEST_ALERT_1_UUID', - started: '2020-01-02T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - }, - trackedAlertsRecovered: {}, - }, - logger, - }) - ); - - expect((await ruleDataClientMock.getWriter()).bulk).toHaveBeenCalledWith( - expect.objectContaining({ - body: [ - // alert document - { - index: { - _id: 'TEST_ALERT_1_UUID', - _index: '.alerts-index-name', - if_primary_term: 3, - if_seq_no: 1, - require_alias: false, - }, - }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_WORKFLOW_STATUS]: 'open', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - - [EVENT_ACTION]: 'active', - [EVENT_KIND]: 'signal', - }), - ], - }) - ); - expect((await ruleDataClientMock.getWriter()).bulk).not.toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.arrayContaining([ - // evaluation documents - { index: {} }, - expect.objectContaining({ - [EVENT_KIND]: 'event', - }), - ]), - }) - ); - expect(logger.warn).toHaveBeenCalledWith( - `Could not update alert TEST_ALERT_0 in partial-.alerts-index-name. Partial and restored alert indices are not supported.` - ); - }); - - it('updates existing documents for recovered alerts', async () => { - const logger = loggerMock.create(); - const ruleDataClientMock = createRuleDataClientMock(); - ruleDataClientMock.getReader().search.mockResolvedValue({ - hits: { - hits: [ - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_UUID]: 'ALERT_0_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [SPACE_IDS]: ['fake-space-id'], - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must show up in the written doc - [TAGS]: ['source-tag1', 'source-tag2'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_UUID]: 'ALERT_1_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [SPACE_IDS]: ['fake-space-id'], - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must not show up in the written doc - [TAGS]: ['source-tag3', 'source-tag4'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - ], - }, - } as any); - const executor = createLifecycleExecutor( - logger, - ruleDataClientMock - )<{}, TestRuleState, never, never, never>(async ({ services, state }) => { - // TEST_ALERT_0 has recovered - services.alertWithLifecycle({ - id: 'TEST_ALERT_1', - fields: {}, - }); - - return { state }; - }); - - await executor( - createDefaultAlertExecutorOptions({ - alertId: 'TEST_ALERT_0', - params: {}, - state: { - wrapped: initialRuleState, - trackedAlerts: { - TEST_ALERT_0: { - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_1: { - alertId: 'TEST_ALERT_1', - alertUuid: 'TEST_ALERT_1_UUID', - started: '2020-01-02T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - }, - trackedAlertsRecovered: {}, - }, - logger, - }) - ); - - expect((await ruleDataClientMock.getWriter()).bulk).toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.arrayContaining([ - // alert document - { index: expect.objectContaining({ _id: 'TEST_ALERT_0_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_STATUS]: ALERT_STATUS_RECOVERED, - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, - [TAGS]: ['source-tag1', 'source-tag2', 'rule-tag1', 'rule-tag2'], - [EVENT_ACTION]: 'close', - [EVENT_KIND]: 'signal', - }), - { index: expect.objectContaining({ _id: 'TEST_ALERT_1_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [EVENT_ACTION]: 'active', - [EVENT_KIND]: 'signal', - [TAGS]: ['source-tag3', 'source-tag4', 'rule-tag1', 'rule-tag2'], - }), - ]), - }) - ); - expect((await ruleDataClientMock.getWriter()).bulk).not.toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.arrayContaining([ - // evaluation documents - { index: {} }, - expect.objectContaining({ - [EVENT_KIND]: 'event', - }), - ]), - }) - ); - }); - - it('does not write alert documents when rule execution is cancelled and feature flags indicate to skip', async () => { - const logger = loggerMock.create(); - const ruleDataClientMock = createRuleDataClientMock(); - const executor = createLifecycleExecutor( - logger, - ruleDataClientMock - )<{}, TestRuleState, never, never, never>(async (options) => { - expect(options.state).toEqual(initialRuleState); - - const nextRuleState: TestRuleState = { - aRuleStateKey: 'NEXT_RULE_STATE_VALUE', - }; - - return { state: nextRuleState }; - }); - - await executor( - createDefaultAlertExecutorOptions({ - params: {}, - state: { wrapped: initialRuleState, trackedAlerts: {}, trackedAlertsRecovered: {} }, - shouldWriteAlerts: false, - logger, - }) - ); - - expect((await ruleDataClientMock.getWriter()).bulk).not.toHaveBeenCalled(); - }); - - it('throws error when writer initialization fails', async () => { - const logger = loggerMock.create(); - const ruleDataClientMock = createRuleDataClientMock(); - ruleDataClientMock.getWriter = jest - .fn() - .mockRejectedValueOnce(new Error('error initializing!')); - const executor = createLifecycleExecutor( - logger, - ruleDataClientMock - )<{}, TestRuleState, never, never, never>(async (options) => { - const nextRuleState: TestRuleState = { - aRuleStateKey: 'NEXT_RULE_STATE_VALUE', - }; - - return { state: nextRuleState }; - }); - - await expect(() => - executor( - createDefaultAlertExecutorOptions({ - params: {}, - state: { wrapped: initialRuleState, trackedAlerts: {}, trackedAlertsRecovered: {} }, - shouldWriteAlerts: false, - logger, - }) - ) - ).rejects.toThrowErrorMatchingInlineSnapshot(`"error initializing!"`); - }); - - describe('updating flappingHistory', () => { - it('sets flapping state to true on a new alert', async () => { - const logger = loggerMock.create(); - const ruleDataClientMock = createRuleDataClientMock(); - const executor = createLifecycleExecutor( - logger, - ruleDataClientMock - )<{}, TestRuleState, never, never, never>(async ({ services, state }) => { - services.alertWithLifecycle({ - id: 'TEST_ALERT_0', - fields: {}, - }); - services.alertWithLifecycle({ - id: 'TEST_ALERT_1', - fields: {}, - }); - - return { state }; - }); - - const { - state: { trackedAlerts, trackedAlertsRecovered }, - } = await executor( - createDefaultAlertExecutorOptions({ - params: {}, - state: { wrapped: initialRuleState, trackedAlerts: {}, trackedAlertsRecovered: {} }, - logger, - }) - ); - - const alerts = pick(trackedAlerts, [ - 'TEST_ALERT_0.flappingHistory', - 'TEST_ALERT_1.flappingHistory', - ]); - expect(alerts).toMatchInlineSnapshot(` - Object { - "TEST_ALERT_0": Object { - "flappingHistory": Array [ - true, - ], - }, - "TEST_ALERT_1": Object { - "flappingHistory": Array [ - true, - ], - }, - } - `); - expect(trackedAlertsRecovered).toMatchInlineSnapshot(`Object {}`); - }); - - it('sets flapping state to false on an alert that is still active', async () => { - const logger = loggerMock.create(); - const ruleDataClientMock = createRuleDataClientMock(); - ruleDataClientMock.getReader().search.mockResolvedValue({ - hits: { - hits: [ - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_UUID]: 'ALERT_0_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_WORKFLOW_STATUS]: 'closed', - [SPACE_IDS]: ['fake-space-id'], - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must show up in the written doc - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_UUID]: 'ALERT_1_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_WORKFLOW_STATUS]: 'open', - [SPACE_IDS]: ['fake-space-id'], - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must not show up in the written doc - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - ], - }, - } as any); - const executor = createLifecycleExecutor( - logger, - ruleDataClientMock - )<{}, TestRuleState, never, never, never>(async ({ services, state }) => { - services.alertWithLifecycle({ - id: 'TEST_ALERT_0', - fields: {}, - }); - services.alertWithLifecycle({ - id: 'TEST_ALERT_1', - fields: {}, - }); - - return { state }; - }); - - const { - state: { trackedAlerts, trackedAlertsRecovered }, - } = await executor( - createDefaultAlertExecutorOptions({ - alertId: 'TEST_ALERT_0', - params: {}, - state: { - wrapped: initialRuleState, - trackedAlerts: { - TEST_ALERT_0: { - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_1: { - alertId: 'TEST_ALERT_1', - alertUuid: 'TEST_ALERT_1_UUID', - started: '2020-01-02T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - }, - trackedAlertsRecovered: {}, - }, - logger, - }) - ); - - const alerts = pick(trackedAlerts, [ - 'TEST_ALERT_0.flappingHistory', - 'TEST_ALERT_1.flappingHistory', - ]); - expect(alerts).toMatchInlineSnapshot(` - Object { - "TEST_ALERT_0": Object { - "flappingHistory": Array [ - false, - ], - }, - "TEST_ALERT_1": Object { - "flappingHistory": Array [ - false, - ], - }, - } - `); - expect(trackedAlertsRecovered).toMatchInlineSnapshot(`Object {}`); - }); - - it('sets flapping state to true on an alert that is active and previously recovered', async () => { - const logger = loggerMock.create(); - const ruleDataClientMock = createRuleDataClientMock(); - ruleDataClientMock.getReader().search.mockResolvedValue({ - hits: { - hits: [ - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_UUID]: 'ALERT_0_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_WORKFLOW_STATUS]: 'closed', - [SPACE_IDS]: ['fake-space-id'], - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must show up in the written doc - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_UUID]: 'ALERT_1_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_WORKFLOW_STATUS]: 'open', - [SPACE_IDS]: ['fake-space-id'], - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must not show up in the written doc - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - ], - }, - } as any); - const executor = createLifecycleExecutor( - logger, - ruleDataClientMock - )<{}, TestRuleState, never, never, never>(async ({ services, state }) => { - services.alertWithLifecycle({ - id: 'TEST_ALERT_0', - fields: {}, - }); - services.alertWithLifecycle({ - id: 'TEST_ALERT_1', - fields: {}, - }); - - return { state }; - }); - - const { - state: { trackedAlerts, trackedAlertsRecovered }, - } = await executor( - createDefaultAlertExecutorOptions({ - alertId: 'TEST_ALERT_0', - params: {}, - state: { - wrapped: initialRuleState, - trackedAlertsRecovered: { - TEST_ALERT_0: { - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_1: { - alertId: 'TEST_ALERT_1', - alertUuid: 'TEST_ALERT_1_UUID', - started: '2020-01-02T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - }, - trackedAlerts: {}, - }, - logger, - }) - ); - - const alerts = pick(trackedAlerts, [ - 'TEST_ALERT_0.flappingHistory', - 'TEST_ALERT_1.flappingHistory', - ]); - expect(alerts).toMatchInlineSnapshot(` - Object { - "TEST_ALERT_0": Object { - "flappingHistory": Array [ - true, - ], - }, - "TEST_ALERT_1": Object { - "flappingHistory": Array [ - true, - ], - }, - } - `); - expect(trackedAlertsRecovered).toMatchInlineSnapshot(`Object {}`); - }); - - it('sets flapping state to true on an alert that is recovered and previously active', async () => { - const logger = loggerMock.create(); - const ruleDataClientMock = createRuleDataClientMock(); - ruleDataClientMock.getReader().search.mockResolvedValue({ - hits: { - hits: [ - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_UUID]: 'ALERT_0_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [SPACE_IDS]: ['fake-space-id'], - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must show up in the written doc - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_UUID]: 'ALERT_1_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [SPACE_IDS]: ['fake-space-id'], - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must not show up in the written doc - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - ], - }, - } as any); - const executor = createLifecycleExecutor( - logger, - ruleDataClientMock - )<{}, TestRuleState, never, never, never>(async ({ services, state }) => { - // TEST_ALERT_0 has recovered - services.alertWithLifecycle({ - id: 'TEST_ALERT_1', - fields: {}, - }); - - return { state }; - }); - - const { - state: { trackedAlerts, trackedAlertsRecovered }, - } = await executor( - createDefaultAlertExecutorOptions({ - alertId: 'TEST_ALERT_0', - params: {}, - state: { - wrapped: initialRuleState, - trackedAlerts: { - TEST_ALERT_0: { - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_1: { - alertId: 'TEST_ALERT_1', - alertUuid: 'TEST_ALERT_1_UUID', - started: '2020-01-02T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - }, - trackedAlertsRecovered: {}, - }, - logger, - }) - ); - - const recovered = pick(trackedAlertsRecovered, ['TEST_ALERT_0.flappingHistory']); - expect(recovered).toMatchInlineSnapshot(` - Object { - "TEST_ALERT_0": Object { - "flappingHistory": Array [ - true, - ], - }, - } - `); - const active = pick(trackedAlerts, ['TEST_ALERT_1.flappingHistory']); - expect(active).toMatchInlineSnapshot(` - Object { - "TEST_ALERT_1": Object { - "flappingHistory": Array [ - false, - ], - }, - } - `); - }); - - it('sets flapping state to false on an alert that is still recovered', async () => { - const logger = loggerMock.create(); - const ruleDataClientMock = createRuleDataClientMock(); - ruleDataClientMock.getReader().search.mockResolvedValue({ - hits: { - hits: [ - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_UUID]: 'ALERT_0_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [SPACE_IDS]: ['fake-space-id'], - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must show up in the written doc - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_UUID]: 'ALERT_1_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [SPACE_IDS]: ['fake-space-id'], - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must not show up in the written doc - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - ], - }, - } as any); - const executor = createLifecycleExecutor( - logger, - ruleDataClientMock - )<{}, TestRuleState, never, never, never>(async ({ services, state }) => { - // TEST_ALERT_0 has recovered - services.alertWithLifecycle({ - id: 'TEST_ALERT_1', - fields: {}, - }); - - return { state }; - }); - - const { - state: { trackedAlerts, trackedAlertsRecovered }, - } = await executor( - createDefaultAlertExecutorOptions({ - alertId: 'TEST_ALERT_0', - params: {}, - state: { - wrapped: initialRuleState, - trackedAlerts: { - TEST_ALERT_1: { - alertId: 'TEST_ALERT_1', - alertUuid: 'TEST_ALERT_1_UUID', - started: '2020-01-02T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - }, - trackedAlertsRecovered: { - TEST_ALERT_0: { - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - }, - }, - logger, - }) - ); - - const recovered = pick(trackedAlertsRecovered, ['TEST_ALERT_0.flappingHistory']); - expect(recovered).toMatchInlineSnapshot(`Object {}`); - const active = pick(trackedAlerts, ['TEST_ALERT_1.flappingHistory']); - expect(active).toMatchInlineSnapshot(` - Object { - "TEST_ALERT_1": Object { - "flappingHistory": Array [ - false, - ], - }, - } - `); - }); - }); - - describe('set maintenance window ids on the document', () => { - const maintenanceWindowIds = ['test-id-1', 'test-id-2']; - - it('updates documents with maintenance window ids for newly firing alerts', async () => { - const logger = loggerMock.create(); - const ruleDataClientMock = createRuleDataClientMock(); - - const executor = createLifecycleExecutor( - logger, - ruleDataClientMock - )<{}, TestRuleState, never, never, never>(async ({ services, state }) => { - services.alertWithLifecycle({ - id: 'TEST_ALERT_0', - fields: { [TAGS]: ['source-tag1', 'source-tag2'] }, - }); - services.alertWithLifecycle({ - id: 'TEST_ALERT_1', - fields: { [TAGS]: ['source-tag3', 'source-tag4'] }, - }); - - return { state }; - }); - - await executor( - createDefaultAlertExecutorOptions({ - params: {}, - state: { wrapped: initialRuleState, trackedAlerts: {}, trackedAlertsRecovered: {} }, - logger, - }) - ); - - expect((await ruleDataClientMock.getWriter()).bulk).toHaveBeenCalledWith( - expect.objectContaining({ - body: [ - // alert documents - { create: { _id: expect.any(String) } }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [EVENT_ACTION]: 'open', - [EVENT_KIND]: 'signal', - [TAGS]: ['source-tag1', 'source-tag2', 'rule-tag1', 'rule-tag2'], - [ALERT_MAINTENANCE_WINDOW_IDS]: maintenanceWindowIds, - }), - { create: { _id: expect.any(String) } }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [EVENT_ACTION]: 'open', - [EVENT_KIND]: 'signal', - [TAGS]: ['source-tag3', 'source-tag4', 'rule-tag1', 'rule-tag2'], - [ALERT_MAINTENANCE_WINDOW_IDS]: maintenanceWindowIds, - }), - ], - }) - ); - expect((await ruleDataClientMock.getWriter()).bulk).not.toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.arrayContaining([ - // evaluation documents - { index: {} }, - expect.objectContaining({ - [EVENT_KIND]: 'event', - }), - ]), - }) - ); - }); - - it('does not update documents with maintenance window ids for repeatedly firing alerts', async () => { - const logger = loggerMock.create(); - const ruleDataClientMock = createRuleDataClientMock(); - ruleDataClientMock.getReader().search.mockResolvedValue({ - hits: { - hits: [ - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_UUID]: 'ALERT_0_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_WORKFLOW_STATUS]: 'closed', - [SPACE_IDS]: ['fake-space-id'], - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must show up in the written doc - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_UUID]: 'ALERT_1_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_WORKFLOW_STATUS]: 'open', - [SPACE_IDS]: ['fake-space-id'], - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must not show up in the written doc - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - ], - }, - } as any); - - const executor = createLifecycleExecutor( - logger, - ruleDataClientMock - )<{}, TestRuleState, never, never, never>(async ({ services, state }) => { - services.alertWithLifecycle({ - id: 'TEST_ALERT_0', - fields: {}, - }); - services.alertWithLifecycle({ - id: 'TEST_ALERT_1', - fields: {}, - }); - - return { state }; - }); - - await executor( - createDefaultAlertExecutorOptions({ - alertId: 'TEST_ALERT_0', - params: {}, - state: { - wrapped: initialRuleState, - trackedAlerts: { - TEST_ALERT_0: { - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_1: { - alertId: 'TEST_ALERT_1', - alertUuid: 'TEST_ALERT_1_UUID', - started: '2020-01-02T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - }, - trackedAlertsRecovered: {}, - }, - logger, - }) - ); - - expect((await ruleDataClientMock.getWriter()).bulk).toHaveBeenCalledWith( - expect.objectContaining({ - body: [ - // alert document - { index: expect.objectContaining({ _id: 'TEST_ALERT_0_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_WORKFLOW_STATUS]: 'closed', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, - [EVENT_ACTION]: 'active', - [EVENT_KIND]: 'signal', - }), - { index: expect.objectContaining({ _id: 'TEST_ALERT_1_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_WORKFLOW_STATUS]: 'open', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [EVENT_ACTION]: 'active', - [EVENT_KIND]: 'signal', - }), - ], - }) - ); - expect((await ruleDataClientMock.getWriter()).bulk).not.toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.arrayContaining([ - // evaluation documents - { index: {} }, - expect.objectContaining({ - [EVENT_KIND]: 'event', - }), - ]), - }) - ); - }); - - it('does not update documents with maintenance window ids for recovered alerts', async () => { - const logger = loggerMock.create(); - const ruleDataClientMock = createRuleDataClientMock(); - ruleDataClientMock.getReader().search.mockResolvedValue({ - hits: { - hits: [ - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_UUID]: 'ALERT_0_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [SPACE_IDS]: ['fake-space-id'], - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must show up in the written doc - [TAGS]: ['source-tag1', 'source-tag2'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_UUID]: 'ALERT_1_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [SPACE_IDS]: ['fake-space-id'], - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must not show up in the written doc - [TAGS]: ['source-tag3', 'source-tag4'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - ], - }, - } as any); - const executor = createLifecycleExecutor( - logger, - ruleDataClientMock - )<{}, TestRuleState, never, never, never>(async ({ services, state }) => { - // TEST_ALERT_0 has recovered - services.alertWithLifecycle({ - id: 'TEST_ALERT_1', - fields: {}, - }); - - return { state }; - }); - - await executor( - createDefaultAlertExecutorOptions({ - alertId: 'TEST_ALERT_0', - params: {}, - state: { - wrapped: initialRuleState, - trackedAlerts: { - TEST_ALERT_0: { - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_1: { - alertId: 'TEST_ALERT_1', - alertUuid: 'TEST_ALERT_1_UUID', - started: '2020-01-02T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - }, - trackedAlertsRecovered: {}, - }, - logger, - }) - ); - - expect((await ruleDataClientMock.getWriter()).bulk).toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.arrayContaining([ - // alert document - { index: expect.objectContaining({ _id: 'TEST_ALERT_0_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_STATUS]: ALERT_STATUS_RECOVERED, - labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, - [TAGS]: ['source-tag1', 'source-tag2', 'rule-tag1', 'rule-tag2'], - [EVENT_ACTION]: 'close', - [EVENT_KIND]: 'signal', - }), - { index: expect.objectContaining({ _id: 'TEST_ALERT_1_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [EVENT_ACTION]: 'active', - [EVENT_KIND]: 'signal', - [TAGS]: ['source-tag3', 'source-tag4', 'rule-tag1', 'rule-tag2'], - }), - ]), - }) - ); - expect((await ruleDataClientMock.getWriter()).bulk).not.toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.arrayContaining([ - // evaluation documents - { index: {} }, - expect.objectContaining({ - [EVENT_KIND]: 'event', - }), - ]), - }) - ); - }); - }); - - describe('set flapping on the document', () => { - const flapping = new Array(16).fill(false).concat([true, true, true, true]); - const notFlapping = new Array(20).fill(false); - - it('updates documents with flapping for active alerts', async () => { - const logger = loggerMock.create(); - const ruleDataClientMock = createRuleDataClientMock(); - ruleDataClientMock.getReader().search.mockResolvedValue({ - hits: { - hits: [ - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_UUID]: 'ALERT_0_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_WORKFLOW_STATUS]: 'closed', - [SPACE_IDS]: ['fake-space-id'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_UUID]: 'ALERT_1_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_WORKFLOW_STATUS]: 'open', - [SPACE_IDS]: ['fake-space-id'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_2', - [ALERT_UUID]: 'ALERT_2_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_WORKFLOW_STATUS]: 'open', - [SPACE_IDS]: ['fake-space-id'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_3', - [ALERT_UUID]: 'ALERT_3_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_WORKFLOW_STATUS]: 'open', - [SPACE_IDS]: ['fake-space-id'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - ], - }, - } as any); - const executor = createLifecycleExecutor( - logger, - ruleDataClientMock - )<{}, TestRuleState, never, never, never>(async ({ services, state }) => { - services.alertWithLifecycle({ - id: 'TEST_ALERT_0', - fields: {}, - }); - services.alertWithLifecycle({ - id: 'TEST_ALERT_1', - fields: {}, - }); - services.alertWithLifecycle({ - id: 'TEST_ALERT_2', - fields: {}, - }); - services.alertWithLifecycle({ - id: 'TEST_ALERT_3', - fields: {}, - }); - - return { state }; - }); - - const serializedAlerts = await executor( - createDefaultAlertExecutorOptions({ - alertId: 'TEST_ALERT_0', - params: {}, - state: { - wrapped: initialRuleState, - trackedAlerts: { - TEST_ALERT_0: { - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: flapping, - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_1: { - alertId: 'TEST_ALERT_1', - alertUuid: 'TEST_ALERT_1_UUID', - started: '2020-01-02T12:00:00.000Z', - flappingHistory: [false, false], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_2: { - alertId: 'TEST_ALERT_2', - alertUuid: 'TEST_ALERT_2_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: flapping, - flapping: true, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_3: { - alertId: 'TEST_ALERT_3', - alertUuid: 'TEST_ALERT_3_UUID', - started: '2020-01-02T12:00:00.000Z', - flappingHistory: [false, false], - flapping: true, - pendingRecoveredCount: 0, - activeCount: 0, - }, - }, - trackedAlertsRecovered: {}, - }, - logger, - }) - ); - - expect(serializedAlerts.state.trackedAlerts).toEqual({ - TEST_ALERT_0: { - activeCount: 1, - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - flapping: true, - flappingHistory: flapping.slice(1).concat([false]), - pendingRecoveredCount: 0, - started: '2020-01-01T12:00:00.000Z', - }, - TEST_ALERT_1: { - activeCount: 1, - alertId: 'TEST_ALERT_1', - alertUuid: 'TEST_ALERT_1_UUID', - flapping: false, - flappingHistory: [false, false, false], - pendingRecoveredCount: 0, - started: '2020-01-02T12:00:00.000Z', - }, - TEST_ALERT_2: { - activeCount: 1, - alertId: 'TEST_ALERT_2', - alertUuid: 'TEST_ALERT_2_UUID', - flapping: true, - flappingHistory: flapping.slice(1).concat([false]), - pendingRecoveredCount: 0, - started: '2020-01-01T12:00:00.000Z', - }, - TEST_ALERT_3: { - activeCount: 1, - alertId: 'TEST_ALERT_3', - alertUuid: 'TEST_ALERT_3_UUID', - flapping: true, - flappingHistory: [false, false, false], - pendingRecoveredCount: 0, - started: '2020-01-02T12:00:00.000Z', - }, - }); - - expect(serializedAlerts.state.trackedAlertsRecovered).toEqual({}); - - expect((await ruleDataClientMock.getWriter()).bulk).toHaveBeenCalledWith( - expect.objectContaining({ - body: [ - // alert document - { index: expect.objectContaining({ _id: 'TEST_ALERT_0_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_WORKFLOW_STATUS]: 'closed', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_FLAPPING]: false, - [EVENT_ACTION]: 'active', - [EVENT_KIND]: 'signal', - }), - { index: expect.objectContaining({ _id: 'TEST_ALERT_1_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_WORKFLOW_STATUS]: 'open', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [EVENT_ACTION]: 'active', - [EVENT_KIND]: 'signal', - [ALERT_FLAPPING]: false, - }), - { index: expect.objectContaining({ _id: 'TEST_ALERT_2_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_2', - [ALERT_WORKFLOW_STATUS]: 'open', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [EVENT_ACTION]: 'active', - [EVENT_KIND]: 'signal', - [ALERT_FLAPPING]: true, - }), - { index: expect.objectContaining({ _id: 'TEST_ALERT_3_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_3', - [ALERT_WORKFLOW_STATUS]: 'open', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [EVENT_ACTION]: 'active', - [EVENT_KIND]: 'signal', - [ALERT_FLAPPING]: true, - }), - ], - }) - ); - }); - - it('updates existing documents for recovered alerts', async () => { - const logger = loggerMock.create(); - const ruleDataClientMock = createRuleDataClientMock(); - ruleDataClientMock.getReader().search.mockResolvedValue({ - hits: { - hits: [ - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_UUID]: 'ALERT_0_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [SPACE_IDS]: ['fake-space-id'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_UUID]: 'ALERT_1_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [SPACE_IDS]: ['fake-space-id'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_2', - [ALERT_UUID]: 'ALERT_2_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [SPACE_IDS]: ['fake-space-id'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_3', - [ALERT_UUID]: 'ALERT_3_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [SPACE_IDS]: ['fake-space-id'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - ], - }, - } as any); - const executor = createLifecycleExecutor( - logger, - ruleDataClientMock - )<{}, TestRuleState, never, never, never>(async ({ services, state }) => { - return { state }; - }); - - const serializedAlerts = await executor( - createDefaultAlertExecutorOptions({ - alertId: 'TEST_ALERT_0', - params: {}, - state: { - wrapped: initialRuleState, - trackedAlerts: { - TEST_ALERT_0: { - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: [true, true, true, true], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_1: { - alertId: 'TEST_ALERT_1', - alertUuid: 'TEST_ALERT_1_UUID', - started: '2020-01-02T12:00:00.000Z', - flappingHistory: notFlapping, - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_2: { - alertId: 'TEST_ALERT_2', - alertUuid: 'TEST_ALERT_2_UUID', - started: '2020-01-02T12:00:00.000Z', - flappingHistory: [true, true], - flapping: true, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_3: { - alertId: 'TEST_ALERT_3', - alertUuid: 'TEST_ALERT_3_UUID', - started: '2020-01-02T12:00:00.000Z', - flappingHistory: notFlapping, - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - }, - trackedAlertsRecovered: {}, - }, - logger, - }) - ); - - expect(serializedAlerts.state.trackedAlerts).toEqual({ - TEST_ALERT_2: { - activeCount: 0, - alertId: 'TEST_ALERT_2', - alertUuid: 'TEST_ALERT_2_UUID', - flapping: true, - flappingHistory: [true, true, true], - pendingRecoveredCount: 1, - started: '2020-01-02T12:00:00.000Z', - }, - }); - - expect(serializedAlerts.state.trackedAlertsRecovered).toEqual({ - TEST_ALERT_0: { - activeCount: 0, - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - flapping: true, - flappingHistory: [true, true, true, true, true], - pendingRecoveredCount: 0, - started: '2020-01-01T12:00:00.000Z', - }, - TEST_ALERT_1: { - activeCount: 0, - alertId: 'TEST_ALERT_1', - alertUuid: 'TEST_ALERT_1_UUID', - flapping: false, - flappingHistory: notFlapping.slice(0, notFlapping.length - 1).concat([true]), - pendingRecoveredCount: 0, - started: '2020-01-02T12:00:00.000Z', - }, - TEST_ALERT_3: { - activeCount: 0, - alertId: 'TEST_ALERT_3', - alertUuid: 'TEST_ALERT_3_UUID', - flapping: false, - flappingHistory: notFlapping.slice(0, notFlapping.length - 1).concat([true]), - pendingRecoveredCount: 0, - started: '2020-01-02T12:00:00.000Z', - }, - }); - - expect((await ruleDataClientMock.getWriter()).bulk).toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.arrayContaining([ - // alert document - { index: expect.objectContaining({ _id: 'TEST_ALERT_0_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_STATUS]: ALERT_STATUS_RECOVERED, - [EVENT_ACTION]: 'close', - [EVENT_KIND]: 'signal', - [ALERT_FLAPPING]: false, - }), - { index: expect.objectContaining({ _id: 'TEST_ALERT_1_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_STATUS]: ALERT_STATUS_RECOVERED, - [EVENT_ACTION]: 'close', - [EVENT_KIND]: 'signal', - [ALERT_FLAPPING]: false, - }), - { index: expect.objectContaining({ _id: 'TEST_ALERT_2_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_2', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [EVENT_ACTION]: 'active', - [EVENT_KIND]: 'signal', - [ALERT_FLAPPING]: true, - }), - { index: expect.objectContaining({ _id: 'TEST_ALERT_3_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_3', - [ALERT_STATUS]: ALERT_STATUS_RECOVERED, - [EVENT_ACTION]: 'close', - [EVENT_KIND]: 'signal', - [ALERT_FLAPPING]: false, - }), - ]), - }) - ); - }); - }); - - describe('set consecutive matches on the document', () => { - it('updates documents with consecutive matches for active alerts', async () => { - const logger = loggerMock.create(); - const ruleDataClientMock = createRuleDataClientMock(); - ruleDataClientMock.getReader().search.mockResolvedValue({ - hits: { - hits: [ - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_UUID]: 'ALERT_0_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_WORKFLOW_STATUS]: 'closed', - [SPACE_IDS]: ['fake-space-id'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_UUID]: 'ALERT_1_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_WORKFLOW_STATUS]: 'open', - [SPACE_IDS]: ['fake-space-id'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_2', - [ALERT_UUID]: 'ALERT_2_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_WORKFLOW_STATUS]: 'open', - [SPACE_IDS]: ['fake-space-id'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_3', - [ALERT_UUID]: 'ALERT_3_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_WORKFLOW_STATUS]: 'open', - [SPACE_IDS]: ['fake-space-id'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - ], - }, - } as any); - const executor = createLifecycleExecutor( - logger, - ruleDataClientMock - )<{}, TestRuleState, never, never, never>(async ({ services, state }) => { - services.alertWithLifecycle({ - id: 'TEST_ALERT_0', - fields: {}, - }); - services.alertWithLifecycle({ - id: 'TEST_ALERT_1', - fields: {}, - }); - services.alertWithLifecycle({ - id: 'TEST_ALERT_2', - fields: {}, - }); - services.alertWithLifecycle({ - id: 'TEST_ALERT_3', - fields: {}, - }); - - return { state }; - }); - - const serializedAlerts = await executor( - createDefaultAlertExecutorOptions({ - alertId: 'TEST_ALERT_0', - params: {}, - state: { - wrapped: initialRuleState, - trackedAlerts: { - TEST_ALERT_0: { - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_1: { - alertId: 'TEST_ALERT_1', - alertUuid: 'TEST_ALERT_1_UUID', - started: '2020-01-02T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_2: { - alertId: 'TEST_ALERT_2', - alertUuid: 'TEST_ALERT_2_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_3: { - alertId: 'TEST_ALERT_3', - alertUuid: 'TEST_ALERT_3_UUID', - started: '2020-01-02T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - }, - trackedAlertsRecovered: {}, - }, - logger, - }) - ); - - expect(serializedAlerts.state.trackedAlerts).toEqual({ - TEST_ALERT_0: { - activeCount: 1, - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - flapping: false, - flappingHistory: [false], - pendingRecoveredCount: 0, - started: '2020-01-01T12:00:00.000Z', - }, - TEST_ALERT_1: { - activeCount: 1, - alertId: 'TEST_ALERT_1', - alertUuid: 'TEST_ALERT_1_UUID', - flapping: false, - flappingHistory: [false], - pendingRecoveredCount: 0, - started: '2020-01-02T12:00:00.000Z', - }, - TEST_ALERT_2: { - activeCount: 1, - alertId: 'TEST_ALERT_2', - alertUuid: 'TEST_ALERT_2_UUID', - flapping: false, - flappingHistory: [false], - pendingRecoveredCount: 0, - started: '2020-01-01T12:00:00.000Z', - }, - TEST_ALERT_3: { - activeCount: 1, - alertId: 'TEST_ALERT_3', - alertUuid: 'TEST_ALERT_3_UUID', - flapping: false, - flappingHistory: [false], - pendingRecoveredCount: 0, - started: '2020-01-02T12:00:00.000Z', - }, - }); - - expect(serializedAlerts.state.trackedAlertsRecovered).toEqual({}); - - expect((await ruleDataClientMock.getWriter()).bulk).toHaveBeenCalledWith( - expect.objectContaining({ - body: [ - // alert document - { index: expect.objectContaining({ _id: 'TEST_ALERT_0_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_WORKFLOW_STATUS]: 'closed', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [ALERT_CONSECUTIVE_MATCHES]: 1, - [EVENT_ACTION]: 'active', - [EVENT_KIND]: 'signal', - }), - { index: expect.objectContaining({ _id: 'TEST_ALERT_1_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_WORKFLOW_STATUS]: 'open', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [EVENT_ACTION]: 'active', - [EVENT_KIND]: 'signal', - [ALERT_CONSECUTIVE_MATCHES]: 1, - }), - { index: expect.objectContaining({ _id: 'TEST_ALERT_2_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_2', - [ALERT_WORKFLOW_STATUS]: 'open', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [EVENT_ACTION]: 'active', - [EVENT_KIND]: 'signal', - [ALERT_CONSECUTIVE_MATCHES]: 1, - }), - { index: expect.objectContaining({ _id: 'TEST_ALERT_3_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_3', - [ALERT_WORKFLOW_STATUS]: 'open', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [EVENT_ACTION]: 'active', - [EVENT_KIND]: 'signal', - [ALERT_CONSECUTIVE_MATCHES]: 1, - }), - ], - }) - ); - }); - - it('updates existing documents for recovered alerts', async () => { - const logger = loggerMock.create(); - const ruleDataClientMock = createRuleDataClientMock(); - ruleDataClientMock.getReader().search.mockResolvedValue({ - hits: { - hits: [ - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_UUID]: 'ALERT_0_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [SPACE_IDS]: ['fake-space-id'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_UUID]: 'ALERT_1_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [SPACE_IDS]: ['fake-space-id'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_2', - [ALERT_UUID]: 'ALERT_2_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [SPACE_IDS]: ['fake-space-id'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - { - _source: { - '@timestamp': '', - [ALERT_INSTANCE_ID]: 'TEST_ALERT_3', - [ALERT_UUID]: 'ALERT_3_UUID', - [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', - [ALERT_RULE_CONSUMER]: 'CONSUMER', - [ALERT_RULE_NAME]: 'NAME', - [ALERT_RULE_PRODUCER]: 'PRODUCER', - [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', - [ALERT_RULE_UUID]: 'RULE_UUID', - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, - [SPACE_IDS]: ['fake-space-id'], - }, - _index: '.alerts-index-name', - _seq_no: 4, - _primary_term: 2, - }, - ], - }, - } as any); - const executor = createLifecycleExecutor( - logger, - ruleDataClientMock - )<{}, TestRuleState, never, never, never>(async ({ services, state }) => { - return { state }; - }); - - const serializedAlerts = await executor( - createDefaultAlertExecutorOptions({ - alertId: 'TEST_ALERT_0', - params: {}, - state: { - wrapped: initialRuleState, - trackedAlerts: { - TEST_ALERT_0: { - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_1: { - alertId: 'TEST_ALERT_1', - alertUuid: 'TEST_ALERT_1_UUID', - started: '2020-01-02T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_2: { - alertId: 'TEST_ALERT_2', - alertUuid: 'TEST_ALERT_2_UUID', - started: '2020-01-02T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - TEST_ALERT_3: { - alertId: 'TEST_ALERT_3', - alertUuid: 'TEST_ALERT_3_UUID', - started: '2020-01-02T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - }, - trackedAlertsRecovered: {}, - }, - logger, - }) - ); - - expect(serializedAlerts.state.trackedAlerts).toEqual({}); - - expect(serializedAlerts.state.trackedAlertsRecovered).toEqual({ - TEST_ALERT_0: { - activeCount: 0, - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - flapping: false, - flappingHistory: [true], - pendingRecoveredCount: 0, - started: '2020-01-01T12:00:00.000Z', - }, - TEST_ALERT_1: { - activeCount: 0, - alertId: 'TEST_ALERT_1', - alertUuid: 'TEST_ALERT_1_UUID', - flapping: false, - flappingHistory: [true], - pendingRecoveredCount: 0, - started: '2020-01-02T12:00:00.000Z', - }, - TEST_ALERT_2: { - activeCount: 0, - alertId: 'TEST_ALERT_2', - alertUuid: 'TEST_ALERT_2_UUID', - flapping: false, - flappingHistory: [true], - pendingRecoveredCount: 0, - started: '2020-01-02T12:00:00.000Z', - }, - TEST_ALERT_3: { - activeCount: 0, - alertId: 'TEST_ALERT_3', - alertUuid: 'TEST_ALERT_3_UUID', - flapping: false, - flappingHistory: [true], - pendingRecoveredCount: 0, - started: '2020-01-02T12:00:00.000Z', - }, - }); - - expect((await ruleDataClientMock.getWriter()).bulk).toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.arrayContaining([ - // alert document - { index: expect.objectContaining({ _id: 'TEST_ALERT_0_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', - [ALERT_STATUS]: ALERT_STATUS_RECOVERED, - [EVENT_ACTION]: 'close', - [EVENT_KIND]: 'signal', - [ALERT_CONSECUTIVE_MATCHES]: 0, - }), - { index: expect.objectContaining({ _id: 'TEST_ALERT_1_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', - [ALERT_STATUS]: ALERT_STATUS_RECOVERED, - [EVENT_ACTION]: 'close', - [EVENT_KIND]: 'signal', - [ALERT_CONSECUTIVE_MATCHES]: 0, - }), - { index: expect.objectContaining({ _id: 'TEST_ALERT_2_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_2', - [ALERT_STATUS]: ALERT_STATUS_RECOVERED, - [EVENT_ACTION]: 'close', - [EVENT_KIND]: 'signal', - [ALERT_CONSECUTIVE_MATCHES]: 0, - }), - { index: expect.objectContaining({ _id: 'TEST_ALERT_3_UUID' }) }, - expect.objectContaining({ - [ALERT_INSTANCE_ID]: 'TEST_ALERT_3', - [ALERT_STATUS]: ALERT_STATUS_RECOVERED, - [EVENT_ACTION]: 'close', - [EVENT_KIND]: 'signal', - [ALERT_CONSECUTIVE_MATCHES]: 0, - }), - ]), - }) - ); - }); - }); -}); - -type TestRuleState = Record & { - aRuleStateKey: string; -}; - -const initialRuleState: TestRuleState = { - aRuleStateKey: 'INITIAL_RULE_STATE_VALUE', -}; diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts deleted file mode 100644 index cdbdf56fabc51..0000000000000 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts +++ /dev/null @@ -1,479 +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 { Logger } from '@kbn/logging'; -import type { PublicContract } from '@kbn/utility-types'; -import { getOrElse } from 'fp-ts/lib/Either'; -import { v4 } from 'uuid'; -import { difference } from 'lodash'; -import { - RuleExecutorOptions, - Alert, - AlertInstanceContext, - AlertInstanceState, - RuleTypeParams, - RuleTypeState, - isValidAlertIndexName, -} from '@kbn/alerting-plugin/server'; -import { isFlapping } from '@kbn/alerting-plugin/server/lib'; -import { wrappedStateRt, WrappedLifecycleRuleState } from '@kbn/alerting-state-types'; -export type { - TrackedLifecycleAlertState, - WrappedLifecycleRuleState, -} from '@kbn/alerting-state-types'; -import { ParsedExperimentalFields } from '../../common/parse_experimental_fields'; -import { ParsedTechnicalFields } from '../../common/parse_technical_fields'; -import { - ALERT_TIME_RANGE, - ALERT_DURATION, - ALERT_END, - ALERT_INSTANCE_ID, - ALERT_START, - ALERT_STATUS, - ALERT_STATUS_ACTIVE, - ALERT_STATUS_RECOVERED, - ALERT_UUID, - ALERT_WORKFLOW_STATUS, - EVENT_ACTION, - EVENT_KIND, - TAGS, - TIMESTAMP, - VERSION, - ALERT_FLAPPING, - ALERT_MAINTENANCE_WINDOW_IDS, -} from '../../common/technical_rule_data_field_names'; -import { CommonAlertFieldNameLatest, CommonAlertIdFieldNameLatest } from '../../common/schemas'; -import { IRuleDataClient } from '../rule_data_client'; -import { AlertExecutorOptionsWithExtraServices } from '../types'; -import { fetchExistingAlerts } from './fetch_existing_alerts'; -import { getCommonAlertFields } from './get_common_alert_fields'; -import { getUpdatedFlappingHistory } from './get_updated_flapping_history'; -import { fetchAlertByAlertUUID } from './fetch_alert_by_uuid'; -import { getAlertsForNotification } from './get_alerts_for_notification'; - -type ImplicitTechnicalFieldName = CommonAlertFieldNameLatest | CommonAlertIdFieldNameLatest; - -type ExplicitTechnicalAlertFields = Partial< - Omit ->; - -type ExplicitAlertFields = Record & // every field can have values of arbitrary types - ExplicitTechnicalAlertFields; // but technical fields must obey their respective type - -export type LifecycleAlertService< - InstanceState extends AlertInstanceState = never, - InstanceContext extends AlertInstanceContext = never, - ActionGroupIds extends string = never -> = (alert: { - id: string; - fields: ExplicitAlertFields; -}) => Alert; - -export interface LifecycleAlertServices< - InstanceState extends AlertInstanceState = never, - InstanceContext extends AlertInstanceContext = never, - ActionGroupIds extends string = never -> { - alertWithLifecycle: LifecycleAlertService; - getAlertStartedDate: (alertInstanceId: string) => string | null; - getAlertUuid: (alertInstanceId: string) => string; - getAlertByAlertUuid: ( - alertUuid: string - ) => Promise | null> | null; -} - -export type LifecycleRuleExecutor< - Params extends RuleTypeParams = never, - State extends RuleTypeState = never, - InstanceState extends AlertInstanceState = never, - InstanceContext extends AlertInstanceContext = never, - ActionGroupIds extends string = never -> = ( - options: AlertExecutorOptionsWithExtraServices< - Params, - State, - InstanceState, - InstanceContext, - ActionGroupIds, - LifecycleAlertServices - > -) => Promise<{ state: State }>; - -export const createLifecycleExecutor = - (logger: Logger, ruleDataClient: PublicContract) => - < - Params extends RuleTypeParams = never, - State extends RuleTypeState = never, - InstanceState extends AlertInstanceState = never, - InstanceContext extends AlertInstanceContext = never, - ActionGroupIds extends string = never - >( - wrappedExecutor: LifecycleRuleExecutor< - Params, - State, - InstanceState, - InstanceContext, - ActionGroupIds - > - ) => - async ( - options: RuleExecutorOptions< - Params, - WrappedLifecycleRuleState, - InstanceState, - InstanceContext, - ActionGroupIds - > - ): Promise<{ state: WrappedLifecycleRuleState }> => { - const { - services: { alertFactory, getMaintenanceWindowIds, shouldWriteAlerts }, - state: previousState, - flappingSettings, - rule, - } = options; - - const ruleDataClientWriter = await ruleDataClient.getWriter(); - - const state = getOrElse( - (): WrappedLifecycleRuleState => ({ - wrapped: previousState as State, - trackedAlerts: {}, - trackedAlertsRecovered: {}, - }) - )(wrappedStateRt().decode(previousState)); - - const commonRuleFields = getCommonAlertFields(options); - - const currentAlerts: Record = {}; - const alertUuidMap: Map = new Map(); - - const lifecycleAlertServices: LifecycleAlertServices< - InstanceState, - InstanceContext, - ActionGroupIds - > = { - alertWithLifecycle: ({ id, fields }) => { - currentAlerts[id] = fields; - const alert = alertFactory.create(id); - const uuid = alert.getUuid(); - alertUuidMap.set(id, uuid); - return alert; - }, - getAlertStartedDate: (alertId: string) => state.trackedAlerts[alertId]?.started ?? null, - getAlertUuid: (alertId: string) => { - const uuid = alertUuidMap.get(alertId); - if (uuid) { - return uuid; - } - - const trackedAlert = state.trackedAlerts[alertId]; - if (trackedAlert) { - return trackedAlert.alertUuid; - } - - const trackedRecoveredAlert = state.trackedAlertsRecovered[alertId]; - if (trackedRecoveredAlert) { - return trackedRecoveredAlert.alertUuid; - } - - const alertInfo = `alert ${alertId} of rule ${rule.ruleTypeId}:${rule.id}`; - logger.warn( - `[Rule Registry] requesting uuid for ${alertInfo} which is not tracked, generating dynamically` - ); - return v4(); - }, - getAlertByAlertUuid: async (alertUuid: string) => { - try { - return await fetchAlertByAlertUUID(ruleDataClient, alertUuid); - } catch (err) { - return null; - } - }, - }; - - const wrappedExecutorResult = await wrappedExecutor({ - ...options, - state: state.wrapped != null ? state.wrapped : ({} as State), - services: { - ...options.services, - ...lifecycleAlertServices, - }, - }); - - const currentAlertIds = Object.keys(currentAlerts); - const trackedAlertIds = Object.keys(state.trackedAlerts); - const trackedAlertRecoveredIds = Object.keys(state.trackedAlertsRecovered); - const newAlertIds = difference(currentAlertIds, trackedAlertIds); - const allAlertIds = [...new Set(currentAlertIds.concat(trackedAlertIds))]; - - const trackedAlertStates = Object.values(state.trackedAlerts); - - logger.debug( - `[Rule Registry] Tracking ${allAlertIds.length} alerts (${newAlertIds.length} new, ${trackedAlertStates.length} previous)` - ); - - // load maintenance window ids if there are new alerts - const maintenanceWindowIds: string[] = allAlertIds.length - ? await getMaintenanceWindowIds() - : []; - - interface TrackedAlertData { - indexName: string; - fields: Partial; - seqNo: number | undefined; - primaryTerm: number | undefined; - } - - const trackedAlertsDataMap: Record = {}; - - if (trackedAlertStates.length) { - const result = await fetchExistingAlerts( - ruleDataClient, - trackedAlertStates, - commonRuleFields - ); - result.forEach((hit) => { - const alertInstanceId = hit._source ? hit._source[ALERT_INSTANCE_ID] : void 0; - if (alertInstanceId && hit._source) { - const alertLabel = `${rule.ruleTypeId}:${rule.id} ${alertInstanceId}`; - if (hit._seq_no == null) { - logger.error(`missing _seq_no on alert instance ${alertLabel}`); - } else if (hit._primary_term == null) { - logger.error(`missing _primary_term on alert instance ${alertLabel}`); - } else { - trackedAlertsDataMap[alertInstanceId] = { - indexName: hit._index, - fields: hit._source, - seqNo: hit._seq_no, - primaryTerm: hit._primary_term, - }; - } - } - }); - } - - const makeEventsDataMapFor = (alertIds: string[]) => - alertIds - .filter((alertId) => { - const alertData = trackedAlertsDataMap[alertId]; - const alertIndex = alertData?.indexName; - if (!alertIndex) { - return true; - } else if (!isValidAlertIndexName(alertIndex)) { - logger.warn( - `Could not update alert ${alertId} in ${alertIndex}. Partial and restored alert indices are not supported.` - ); - return false; - } - return true; - }) - .map((alertId) => { - const alertData = trackedAlertsDataMap[alertId]; - const currentAlertData = currentAlerts[alertId]; - const trackedAlert = state.trackedAlerts[alertId]; - - if (!alertData) { - logger.debug(`[Rule Registry] Could not find alert data for ${alertId}`); - } - - const isNew = !trackedAlert; - const isRecovered = !currentAlertData; - const isActive = !isRecovered; - - const flappingHistory = getUpdatedFlappingHistory( - flappingSettings, - alertId, - state, - isNew, - isRecovered, - isActive, - trackedAlertRecoveredIds - ); - - const { alertUuid, started, flapping, pendingRecoveredCount, activeCount } = !isNew - ? state.trackedAlerts[alertId] - : { - alertUuid: lifecycleAlertServices.getAlertUuid(alertId), - started: commonRuleFields[TIMESTAMP], - flapping: state.trackedAlertsRecovered[alertId] - ? state.trackedAlertsRecovered[alertId].flapping - : false, - pendingRecoveredCount: 0, - activeCount: 0, - }; - - const event: ParsedTechnicalFields & ParsedExperimentalFields = { - ...alertData?.fields, - ...commonRuleFields, - ...currentAlertData, - [ALERT_DURATION]: (options.startedAt.getTime() - new Date(started).getTime()) * 1000, - [ALERT_TIME_RANGE]: isRecovered - ? { - gte: started, - lte: commonRuleFields[TIMESTAMP], - } - : { gte: started }, - [ALERT_INSTANCE_ID]: alertId, - [ALERT_START]: started, - [ALERT_UUID]: alertUuid, - [ALERT_STATUS]: isRecovered ? ALERT_STATUS_RECOVERED : ALERT_STATUS_ACTIVE, - [ALERT_WORKFLOW_STATUS]: alertData?.fields[ALERT_WORKFLOW_STATUS] ?? 'open', - [EVENT_KIND]: 'signal', - [EVENT_ACTION]: isNew ? 'open' : isActive ? 'active' : 'close', - [TAGS]: Array.from( - new Set([ - ...(currentAlertData?.tags ?? []), - ...(alertData?.fields[TAGS] ?? []), - ...(options.rule.tags ?? []), - ]) - ), - [VERSION]: ruleDataClient.kibanaVersion, - [ALERT_FLAPPING]: flapping, - ...(isRecovered ? { [ALERT_END]: commonRuleFields[TIMESTAMP] } : {}), - ...(isNew && maintenanceWindowIds?.length - ? { [ALERT_MAINTENANCE_WINDOW_IDS]: maintenanceWindowIds } - : {}), - }; - - return { - indexName: alertData?.indexName, - seqNo: alertData?.seqNo, - primaryTerm: alertData?.primaryTerm, - event, - flappingHistory, - flapping, - pendingRecoveredCount, - activeCount, - }; - }); - - const trackedEventsToIndex = makeEventsDataMapFor(trackedAlertIds); - const newEventsToIndex = makeEventsDataMapFor(newAlertIds); - const trackedRecoveredEventsToIndex = makeEventsDataMapFor(trackedAlertRecoveredIds); - const allEventsToIndex = getAlertsForNotification( - flappingSettings, - rule.alertDelay?.active ?? 0, - trackedEventsToIndex, - newEventsToIndex, - { maintenanceWindowIds, timestamp: commonRuleFields[TIMESTAMP] } - ); - - // Only write alerts if: - // - writing is enabled - // AND - // - rule execution has not been cancelled due to timeout - // OR - // - if execution has been cancelled due to timeout, if feature flags are configured to write alerts anyway - const writeAlerts = ruleDataClient.isWriteEnabled() && shouldWriteAlerts(); - - if (allEventsToIndex.length > 0 && writeAlerts) { - logger.debug(`[Rule Registry] Preparing to index ${allEventsToIndex.length} alerts.`); - - await ruleDataClientWriter.bulk({ - body: allEventsToIndex.flatMap(({ event, indexName, seqNo, primaryTerm }) => [ - indexName - ? { - index: { - _id: event[ALERT_UUID]!, - _index: indexName, - if_seq_no: seqNo, - if_primary_term: primaryTerm, - require_alias: false, - }, - } - : { - create: { - _id: event[ALERT_UUID]!, - }, - }, - event, - ]), - refresh: true, - }); - } else { - logger.debug( - `[Rule Registry] Not indexing ${allEventsToIndex.length} alerts because writing has been disabled.` - ); - } - - const nextTrackedAlerts = Object.fromEntries( - [...newEventsToIndex, ...trackedEventsToIndex] - .filter(({ event }) => event[ALERT_STATUS] !== ALERT_STATUS_RECOVERED) - .map( - ({ - event, - flappingHistory, - flapping: isCurrentlyFlapping, - pendingRecoveredCount, - activeCount, - }) => { - const alertId = event[ALERT_INSTANCE_ID]!; - const alertUuid = event[ALERT_UUID]!; - const started = new Date(event[ALERT_START]!).toISOString(); - const flapping = isFlapping(flappingSettings, flappingHistory, isCurrentlyFlapping); - return [ - alertId, - { - alertId, - alertUuid, - started, - flappingHistory, - flapping, - pendingRecoveredCount, - activeCount, - }, - ]; - } - ) - ); - - const nextTrackedAlertsRecovered = Object.fromEntries( - [...allEventsToIndex, ...trackedRecoveredEventsToIndex] - .filter( - ({ event, flappingHistory, flapping }) => - // return recovered alerts if they are flapping or if the flapping array is not at capacity - // this is a space saving effort that will stop tracking a recovered alert if it wasn't flapping and doesn't have state changes - // in the last max capcity number of executions - event[ALERT_STATUS] === ALERT_STATUS_RECOVERED && - (flapping || flappingHistory.filter((f: boolean) => f).length > 0) - ) - .map( - ({ - event, - flappingHistory, - flapping: isCurrentlyFlapping, - pendingRecoveredCount, - activeCount, - }) => { - const alertId = event[ALERT_INSTANCE_ID]!; - const alertUuid = event[ALERT_UUID]!; - const started = new Date(event[ALERT_START]!).toISOString(); - const flapping = isFlapping(flappingSettings, flappingHistory, isCurrentlyFlapping); - return [ - alertId, - { - alertId, - alertUuid, - started, - flappingHistory, - flapping, - pendingRecoveredCount, - activeCount, - }, - ]; - } - ) - ); - - return { - state: { - wrapped: wrappedExecutorResult?.state ?? ({} as State), - trackedAlerts: writeAlerts ? nextTrackedAlerts : {}, - trackedAlertsRecovered: writeAlerts ? nextTrackedAlertsRecovered : {}, - }, - }; - }; diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_executor_mock.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_executor_mock.ts deleted file mode 100644 index bf0d98d5156af..0000000000000 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_executor_mock.ts +++ /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 { - RuleTypeParams, - RuleTypeState, - AlertInstanceState, - AlertInstanceContext, -} from '@kbn/alerting-plugin/server'; -import { AlertExecutorOptionsWithExtraServices } from '../types'; - -import { LifecycleAlertServices, LifecycleRuleExecutor } from './create_lifecycle_executor'; - -export const createLifecycleRuleExecutorMock = - < - Params extends RuleTypeParams = never, - State extends RuleTypeState = never, - InstanceState extends AlertInstanceState = never, - InstanceContext extends AlertInstanceContext = never, - ActionGroupIds extends string = never - >( - executor: LifecycleRuleExecutor - ) => - async ( - options: AlertExecutorOptionsWithExtraServices< - Params, - State, - InstanceState, - InstanceContext, - ActionGroupIds, - LifecycleAlertServices - > - ) => - await executor(options); diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts deleted file mode 100644 index 6dbc33b666497..0000000000000 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts +++ /dev/null @@ -1,512 +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 { schema } from '@kbn/config-schema'; -import { - ALERT_DURATION, - ALERT_STATUS, - ALERT_STATUS_ACTIVE, - ALERT_STATUS_RECOVERED, - ALERT_UUID, - ALERT_TIME_RANGE, -} from '@kbn/rule-data-utils'; -import { loggerMock } from '@kbn/logging-mocks'; -import { castArray, omit } from 'lodash'; -import { createRuleDataClientMock } from '../rule_data_client/rule_data_client.mock'; -import { createLifecycleRuleTypeFactory } from './create_lifecycle_rule_type_factory'; -import { ISearchStartSearchSource } from '@kbn/data-plugin/common'; -import { SharePluginStart } from '@kbn/share-plugin/server'; -import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; -import { DEFAULT_FLAPPING_SETTINGS } from '@kbn/alerting-plugin/common/rules_settings'; - -type RuleTestHelpers = ReturnType; - -function createRule(shouldWriteAlerts: boolean = true) { - const ruleDataClientMock = createRuleDataClientMock(); - - const factory = createLifecycleRuleTypeFactory({ - ruleDataClient: ruleDataClientMock, - logger: loggerMock.create(), - }); - - let nextAlerts: Array<{ id: string; fields: Record }> = []; - - const type = factory({ - actionGroups: [ - { - id: 'warning', - name: 'warning', - }, - ], - actionVariables: { - context: [], - params: [], - state: [], - }, - defaultActionGroupId: 'warning', - executor: async ({ services }) => { - nextAlerts.forEach((alert) => { - services.alertWithLifecycle(alert); - }); - nextAlerts = []; - return { state: {} }; - }, - id: 'ruleTypeId', - isExportable: true, - minimumLicenseRequired: 'basic', - name: 'ruleTypeName', - category: 'test', - producer: 'producer', - validate: { - params: schema.object( - {}, - { - unknowns: 'allow', - } - ), - }, - }); - - let state: Record = {}; - let previousStartedAt: Date | null; - const createdAt = new Date('2021-06-16T09:00:00.000Z'); - - const scheduleActions = jest.fn(); - - let uuidCounter = 1; - const getUuid = jest.fn(() => `uuid-${uuidCounter++}`); - - const alertFactory = { - create: () => { - return { - scheduleActions, - getUuid, - } as any; - }, - alertLimit: { - getValue: () => 1000, - setLimitReached: () => {}, - }, - done: () => ({ getRecoveredAlerts: () => [] }), - }; - - return { - alertWithLifecycle: async (alerts: Array<{ id: string; fields: Record }>) => { - nextAlerts = alerts; - - const startedAt = new Date((previousStartedAt ?? createdAt).getTime() + 60000); - - scheduleActions.mockClear(); - - ({ state } = ((await type.executor({ - executionId: 'b33f65d7-6e8b-4aae-8d20-c93613dec9f9', - logger: loggerMock.create(), - namespace: 'namespace', - params: { threshold: 1, operator: '>' }, - previousStartedAt, - rule: { - id: 'alertId', - actions: [], - consumer: 'consumer', - createdAt, - createdBy: 'createdBy', - enabled: true, - muteAll: false, - name: 'name', - notifyWhen: 'onActionGroupChange', - producer: 'producer', - revision: 0, - ruleTypeId: 'ruleTypeId', - ruleTypeName: 'ruleTypeName', - schedule: { - interval: '1m', - }, - snoozeSchedule: [], - tags: ['tags'], - throttle: null, - updatedAt: createdAt, - updatedBy: 'updatedBy', - }, - services: { - alertsClient: null, - alertFactory, - savedObjectsClient: {} as any, - scopedClusterClient: {} as any, - search: {} as any, - getMaintenanceWindowIds: async () => [], - getSearchSourceClient: async () => ({} as ISearchStartSearchSource), - shouldStopExecution: () => false, - shouldWriteAlerts: () => shouldWriteAlerts, - uiSettingsClient: {} as any, - share: {} as SharePluginStart, - getDataViews: async () => dataViewPluginMocks.createStartContract(), - }, - spaceId: 'spaceId', - startedAt, - startedAtOverridden: false, - state, - flappingSettings: DEFAULT_FLAPPING_SETTINGS, - getTimeRange: () => { - const date = new Date(Date.now()).toISOString(); - return { dateStart: date, dateEnd: date }; - }, - })) ?? {}) as Record); - - previousStartedAt = startedAt; - }, - scheduleActions, - ruleDataClientMock, - }; -} - -describe('createLifecycleRuleTypeFactory', () => { - describe('with a new rule', () => { - let helpers: RuleTestHelpers; - - beforeEach(() => { - helpers = createRule(); - }); - - describe('when writing is disabled', () => { - beforeEach(() => { - helpers.ruleDataClientMock.isWriteEnabled.mockReturnValue(false); - }); - - it("doesn't persist anything", async () => { - await helpers.alertWithLifecycle([ - { - id: 'opbeans-java', - fields: { - 'service.name': 'opbeans-java', - }, - }, - ]); - - expect((await helpers.ruleDataClientMock.getWriter()).bulk).toHaveBeenCalledTimes(0); - }); - }); - - describe('when rule is cancelled due to timeout and config flags indicate to skip actions', () => { - beforeEach(() => { - helpers = createRule(false); - helpers.ruleDataClientMock.isWriteEnabled.mockReturnValue(true); - }); - - it("doesn't persist anything", async () => { - await helpers.alertWithLifecycle([ - { - id: 'opbeans-java', - fields: { - 'service.name': 'opbeans-java', - }, - }, - ]); - - expect((await helpers.ruleDataClientMock.getWriter()).bulk).toHaveBeenCalledTimes(0); - }); - }); - - describe('when alerts are new', () => { - beforeEach(async () => { - await helpers.alertWithLifecycle([ - { - id: 'opbeans-java', - fields: { - 'service.name': 'opbeans-java', - }, - }, - { - id: 'opbeans-node', - fields: { - 'service.name': 'opbeans-node', - }, - }, - ]); - }); - - it('writes the correct alerts', async () => { - expect((await helpers.ruleDataClientMock.getWriter()).bulk).toHaveBeenCalledTimes(1); - - const body = (await helpers.ruleDataClientMock.getWriter()).bulk.mock.calls[0][0].body!; - - const documents: any[] = body.filter((op: any) => !isOpDoc(op)); - - const evaluationDocuments = documents.filter((doc) => doc['event.kind'] === 'event'); - const alertDocuments = documents.filter((doc) => doc['event.kind'] === 'signal'); - - expect(evaluationDocuments.length).toBe(0); - expect(alertDocuments.length).toBe(2); - - expect( - alertDocuments.every((doc) => doc[ALERT_STATUS] === ALERT_STATUS_ACTIVE) - ).toBeTruthy(); - - expect(alertDocuments.every((doc) => doc[ALERT_DURATION] === 0)).toBeTruthy(); - - expect(alertDocuments.every((doc) => doc['event.action'] === 'open')).toBeTruthy(); - - expect(documents.map((doc) => omit(doc, ALERT_UUID))).toMatchInlineSnapshot(` - Array [ - Object { - "@timestamp": "2021-06-16T09:01:00.000Z", - "event.action": "open", - "event.kind": "signal", - "kibana.alert.consecutive_matches": 1, - "kibana.alert.duration.us": 0, - "kibana.alert.flapping": false, - "kibana.alert.instance.id": "opbeans-java", - "kibana.alert.rule.category": "ruleTypeName", - "kibana.alert.rule.consumer": "consumer", - "kibana.alert.rule.execution.uuid": "b33f65d7-6e8b-4aae-8d20-c93613dec9f9", - "kibana.alert.rule.name": "name", - "kibana.alert.rule.parameters": Object { - "operator": ">", - "threshold": 1, - }, - "kibana.alert.rule.producer": "producer", - "kibana.alert.rule.revision": 0, - "kibana.alert.rule.rule_type_id": "ruleTypeId", - "kibana.alert.rule.tags": Array [ - "tags", - ], - "kibana.alert.rule.uuid": "alertId", - "kibana.alert.start": "2021-06-16T09:01:00.000Z", - "kibana.alert.status": "active", - "kibana.alert.time_range": Object { - "gte": "2021-06-16T09:01:00.000Z", - }, - "kibana.alert.workflow_status": "open", - "kibana.space_ids": Array [ - "spaceId", - ], - "kibana.version": "7.16.0", - "service.name": "opbeans-java", - "tags": Array [ - "tags", - ], - }, - Object { - "@timestamp": "2021-06-16T09:01:00.000Z", - "event.action": "open", - "event.kind": "signal", - "kibana.alert.consecutive_matches": 1, - "kibana.alert.duration.us": 0, - "kibana.alert.flapping": false, - "kibana.alert.instance.id": "opbeans-node", - "kibana.alert.rule.category": "ruleTypeName", - "kibana.alert.rule.consumer": "consumer", - "kibana.alert.rule.execution.uuid": "b33f65d7-6e8b-4aae-8d20-c93613dec9f9", - "kibana.alert.rule.name": "name", - "kibana.alert.rule.parameters": Object { - "operator": ">", - "threshold": 1, - }, - "kibana.alert.rule.producer": "producer", - "kibana.alert.rule.revision": 0, - "kibana.alert.rule.rule_type_id": "ruleTypeId", - "kibana.alert.rule.tags": Array [ - "tags", - ], - "kibana.alert.rule.uuid": "alertId", - "kibana.alert.start": "2021-06-16T09:01:00.000Z", - "kibana.alert.status": "active", - "kibana.alert.time_range": Object { - "gte": "2021-06-16T09:01:00.000Z", - }, - "kibana.alert.workflow_status": "open", - "kibana.space_ids": Array [ - "spaceId", - ], - "kibana.version": "7.16.0", - "service.name": "opbeans-node", - "tags": Array [ - "tags", - ], - }, - ] - `); - }); - }); - - describe('when alerts are active', () => { - beforeEach(async () => { - await helpers.alertWithLifecycle([ - { - id: 'opbeans-java', - fields: { - 'service.name': 'opbeans-java', - }, - }, - { - id: 'opbeans-node', - fields: { - 'service.name': 'opbeans-node', - }, - }, - ]); - - // TODO mock the resolved value before calling alertWithLifecycle again - const lastOpbeansNodeDoc = ( - await helpers.ruleDataClientMock.getWriter() - ).bulk.mock.calls[0][0].body - ?.concat() - .reverse() - .find((doc: any) => !isOpDoc(doc) && doc['service.name'] === 'opbeans-node') as Record< - string, - any - >; - - // @ts-ignore 4.3.5 upgrade - helpers.ruleDataClientMock.getReader().search.mockResolvedValueOnce({ - hits: { - hits: [{ _source: lastOpbeansNodeDoc } as any], - total: { - value: 1, - relation: 'eq', - }, - }, - took: 0, - timed_out: false, - _shards: { - failed: 0, - successful: 1, - total: 1, - }, - }); - - await helpers.alertWithLifecycle([ - { - id: 'opbeans-java', - fields: { - 'service.name': 'opbeans-java', - }, - }, - { - id: 'opbeans-node', - fields: { - 'service.name': 'opbeans-node', - 'kibana.alert.workflow_status': 'closed', - }, - }, - ]); - }); - - it('writes the correct alerts', async () => { - expect((await helpers.ruleDataClientMock.getWriter()).bulk).toHaveBeenCalledTimes(2); - const body = (await helpers.ruleDataClientMock.getWriter()).bulk.mock.calls[1][0].body!; - - const documents: any[] = body.filter((op: any) => !isOpDoc(op)); - - const evaluationDocuments = documents.filter((doc) => doc['event.kind'] === 'event'); - const alertDocuments = documents.filter((doc) => doc['event.kind'] === 'signal'); - - expect(evaluationDocuments.length).toBe(0); - expect(alertDocuments.length).toBe(2); - - expect( - alertDocuments.every((doc) => doc[ALERT_STATUS] === ALERT_STATUS_ACTIVE) - ).toBeTruthy(); - expect(alertDocuments.every((doc) => doc['event.action'] === 'active')).toBeTruthy(); - - expect(alertDocuments.every((doc) => doc[ALERT_DURATION] > 0)).toBeTruthy(); - }); - }); - - describe('when alerts recover', () => { - beforeEach(async () => { - await helpers.alertWithLifecycle([ - { - id: 'opbeans-java', - fields: { - 'service.name': 'opbeans-java', - }, - }, - { - id: 'opbeans-node', - fields: { - 'service.name': 'opbeans-node', - }, - }, - ]); - - const lastOpbeansNodeDoc = ( - await helpers.ruleDataClientMock.getWriter() - ).bulk.mock.calls[0][0].body - ?.concat() - .reverse() - .find((doc: any) => !isOpDoc(doc) && doc['service.name'] === 'opbeans-node') as Record< - string, - any - >; - - helpers.ruleDataClientMock.getReader().search.mockResolvedValueOnce({ - hits: { - hits: [ - { - _source: lastOpbeansNodeDoc, - _index: '.alerts-a', - _primary_term: 4, - _seq_no: 2, - } as any, - ], - total: { - value: 1, - relation: 'eq', - }, - }, - took: 0, - timed_out: false, - _shards: { - failed: 0, - successful: 1, - total: 1, - }, - }); - - await helpers.alertWithLifecycle([ - { - id: 'opbeans-java', - fields: { - 'service.name': 'opbeans-java', - }, - }, - ]); - }); - - it('writes the correct alerts', async () => { - expect((await helpers.ruleDataClientMock.getWriter()).bulk).toHaveBeenCalledTimes(2); - - const body = (await helpers.ruleDataClientMock.getWriter()).bulk.mock.calls[1][0].body!; - - const documents: any[] = body.filter((op: any) => !isOpDoc(op)); - - const opbeansJavaAlertDoc = documents.find( - (doc) => castArray(doc['service.name'])[0] === 'opbeans-java' - ); - const opbeansNodeAlertDoc = documents.find( - (doc) => castArray(doc['service.name'])[0] === 'opbeans-node' - ); - - expect(opbeansJavaAlertDoc['event.action']).toBe('active'); - expect(opbeansJavaAlertDoc[ALERT_STATUS]).toBe(ALERT_STATUS_ACTIVE); - - expect(opbeansNodeAlertDoc['event.action']).toBe('close'); - expect(opbeansNodeAlertDoc[ALERT_STATUS]).toBe(ALERT_STATUS_RECOVERED); - expect(opbeansNodeAlertDoc[ALERT_TIME_RANGE]).toEqual({ - gte: '2021-06-16T09:01:00.000Z', - lte: '2021-06-16T09:02:00.000Z', - }); - }); - }); - }); -}); - -function isOpDoc(doc: any) { - if (doc?.index?._id) return true; - if (doc?.create?._id) return true; - return false; -} diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.ts deleted file mode 100644 index 7f1be5ff54f83..0000000000000 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { Logger } from '@kbn/logging'; -import { - AlertInstanceContext, - AlertInstanceState, - RuleTypeParams, - RuleTypeState, -} from '@kbn/alerting-plugin/common'; -import { IRuleDataClient } from '../rule_data_client'; -import { AlertTypeWithExecutor } from '../types'; -import { createLifecycleExecutor, LifecycleAlertServices } from './create_lifecycle_executor'; - -export const createLifecycleRuleTypeFactory = - ({ logger, ruleDataClient }: { logger: Logger; ruleDataClient: IRuleDataClient }) => - < - TParams extends RuleTypeParams, - TAlertInstanceState extends AlertInstanceState, - TAlertInstanceContext extends AlertInstanceContext, - TActionGroupIds extends string, - TServices extends LifecycleAlertServices< - TAlertInstanceState, - TAlertInstanceContext, - TActionGroupIds - > - >( - type: AlertTypeWithExecutor - ): AlertTypeWithExecutor => { - const createBoundLifecycleExecutor = createLifecycleExecutor(logger, ruleDataClient); - const executor = createBoundLifecycleExecutor< - TParams, - RuleTypeState, - AlertInstanceState, - TAlertInstanceContext, - string - >(type.executor as any); - return { - ...type, - executor: executor as any, - }; - }; diff --git a/x-pack/plugins/rule_registry/server/utils/get_updated_flapping_history.test.ts b/x-pack/plugins/rule_registry/server/utils/get_updated_flapping_history.test.ts deleted file mode 100644 index 84685779186d9..0000000000000 --- a/x-pack/plugins/rule_registry/server/utils/get_updated_flapping_history.test.ts +++ /dev/null @@ -1,207 +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 { - DEFAULT_FLAPPING_SETTINGS, - DISABLE_FLAPPING_SETTINGS, -} from '@kbn/alerting-plugin/common/rules_settings'; -import { getUpdatedFlappingHistory } from './get_updated_flapping_history'; - -describe('getUpdatedFlappingHistory', () => { - type TestRuleState = Record & { - aRuleStateKey: string; - }; - const initialRuleState: TestRuleState = { - aRuleStateKey: 'INITIAL_RULE_STATE_VALUE', - }; - - test('sets flapping state to true if the alert is new', () => { - const state = { wrapped: initialRuleState, trackedAlerts: {}, trackedAlertsRecovered: {} }; - expect( - getUpdatedFlappingHistory( - DEFAULT_FLAPPING_SETTINGS, - 'TEST_ALERT_0', - state, - true, - false, - false, - [] - ) - ).toMatchInlineSnapshot(` - Array [ - true, - ] - `); - }); - - test('sets flapping state to false on an alert that is still active', () => { - const state = { - wrapped: initialRuleState, - trackedAlerts: { - TEST_ALERT_0: { - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - }, - trackedAlertsRecovered: {}, - }; - expect( - getUpdatedFlappingHistory( - DEFAULT_FLAPPING_SETTINGS, - 'TEST_ALERT_0', - state, - false, - false, - true, - [] - ) - ).toMatchInlineSnapshot(` - Array [ - false, - ] - `); - }); - - test('sets flapping state to true on an alert that is active and previously recovered', () => { - const state = { - wrapped: initialRuleState, - trackedAlertsRecovered: { - TEST_ALERT_0: { - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - }, - trackedAlerts: {}, - }; - const recoveredIds = ['TEST_ALERT_0']; - expect( - getUpdatedFlappingHistory( - DEFAULT_FLAPPING_SETTINGS, - 'TEST_ALERT_0', - state, - true, - false, - true, - recoveredIds - ) - ).toMatchInlineSnapshot(` - Array [ - true, - ] - `); - expect(recoveredIds).toEqual([]); - }); - - test('sets flapping state to true on an alert that is recovered and previously active', () => { - const state = { - wrapped: initialRuleState, - trackedAlerts: { - TEST_ALERT_0: { - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - }, - trackedAlertsRecovered: {}, - }; - const recoveredIds = ['TEST_ALERT_0']; - expect( - getUpdatedFlappingHistory( - DEFAULT_FLAPPING_SETTINGS, - 'TEST_ALERT_0', - state, - false, - true, - false, - recoveredIds - ) - ).toMatchInlineSnapshot(` - Array [ - true, - ] - `); - expect(recoveredIds).toEqual(['TEST_ALERT_0']); - }); - - test('sets flapping state to false on an alert that is still recovered', () => { - const state = { - wrapped: initialRuleState, - trackedAlerts: {}, - trackedAlertsRecovered: { - TEST_ALERT_0: { - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - }, - }; - const recoveredIds = ['TEST_ALERT_0']; - expect( - getUpdatedFlappingHistory( - DEFAULT_FLAPPING_SETTINGS, - 'TEST_ALERT_0', - state, - false, - true, - false, - recoveredIds - ) - ).toMatchInlineSnapshot(` - Array [ - false, - ] - `); - expect(recoveredIds).toEqual(['TEST_ALERT_0']); - }); - - test('does not set flapping state if flapping is not enabled', () => { - const state = { - wrapped: initialRuleState, - trackedAlerts: {}, - trackedAlertsRecovered: { - TEST_ALERT_0: { - alertId: 'TEST_ALERT_0', - alertUuid: 'TEST_ALERT_0_UUID', - started: '2020-01-01T12:00:00.000Z', - flappingHistory: [], - flapping: false, - pendingRecoveredCount: 0, - activeCount: 0, - }, - }, - }; - expect( - getUpdatedFlappingHistory( - DISABLE_FLAPPING_SETTINGS, - 'TEST_ALERT_0', - state, - false, - true, - false, - ['TEST_ALERT_0'] - ) - ).toMatchInlineSnapshot(`Array []`); - }); -}); diff --git a/x-pack/plugins/rule_registry/server/utils/get_updated_flapping_history.ts b/x-pack/plugins/rule_registry/server/utils/get_updated_flapping_history.ts deleted file mode 100644 index 854f919722330..0000000000000 --- a/x-pack/plugins/rule_registry/server/utils/get_updated_flapping_history.ts +++ /dev/null @@ -1,64 +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 { RuleTypeState } from '@kbn/alerting-plugin/common'; -import { RulesSettingsFlappingProperties } from '@kbn/alerting-plugin/common/rules_settings'; -import { updateFlappingHistory } from '@kbn/alerting-plugin/server/lib'; -import { remove } from 'lodash'; -import { WrappedLifecycleRuleState } from './create_lifecycle_executor'; - -export function getUpdatedFlappingHistory( - flappingSettings: RulesSettingsFlappingProperties, - alertId: string, - state: WrappedLifecycleRuleState, - isNew: boolean, - isRecovered: boolean, - isActive: boolean, - recoveredIds: string[] -) { - // duplicating this logic to determine flapping at this level - let flappingHistory: boolean[] = []; - if (flappingSettings.enabled) { - if (isRecovered) { - if (state.trackedAlerts[alertId]) { - // this alert has flapped from active to recovered - flappingHistory = updateFlappingHistory( - flappingSettings, - state.trackedAlerts[alertId].flappingHistory, - true - ); - } else if (state.trackedAlertsRecovered[alertId]) { - // this alert is still recovered - flappingHistory = updateFlappingHistory( - flappingSettings, - state.trackedAlertsRecovered[alertId].flappingHistory, - false - ); - } - } else if (isNew) { - if (state.trackedAlertsRecovered[alertId]) { - // this alert has flapped from recovered to active - flappingHistory = updateFlappingHistory( - flappingSettings, - state.trackedAlertsRecovered[alertId].flappingHistory, - true - ); - remove(recoveredIds, (id) => id === alertId); - } else { - flappingHistory = updateFlappingHistory(flappingSettings, [], true); - } - } else if (isActive) { - // this alert is still active - flappingHistory = updateFlappingHistory( - flappingSettings, - state.trackedAlerts[alertId].flappingHistory, - false - ); - } - } - return flappingHistory; -} diff --git a/x-pack/plugins/rule_registry/server/utils/lifecycle_alert_services.mock.ts b/x-pack/plugins/rule_registry/server/utils/lifecycle_alert_services.mock.ts deleted file mode 100644 index 9324bcfd76cb4..0000000000000 --- a/x-pack/plugins/rule_registry/server/utils/lifecycle_alert_services.mock.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { AlertInstanceContext, AlertInstanceState } from '@kbn/alerting-plugin/server'; -import { alertsMock } from '@kbn/alerting-plugin/server/mocks'; -import { LifecycleAlertServices } from './create_lifecycle_executor'; - -/** - * This wraps the alerts to enable the preservation of the generic type - * arguments of the factory function. - **/ -class AlertsMockWrapper< - InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext -> { - createAlertServices() { - return alertsMock.createRuleExecutorServices(); - } -} - -type AlertServices< - InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext -> = ReturnType['createAlertServices']>; - -export const createLifecycleAlertServicesMock = < - InstanceState extends AlertInstanceState = never, - InstanceContext extends AlertInstanceContext = never, - ActionGroupIds extends string = never ->( - alertServices: AlertServices -): LifecycleAlertServices => ({ - alertWithLifecycle: ({ id }) => alertServices.alertFactory.create(id), - getAlertStartedDate: jest.fn((id: string) => null), - getAlertUuid: jest.fn((id: string) => 'mock-alert-uuid'), - getAlertByAlertUuid: jest.fn((id: string) => Promise.resolve(null)), -}); diff --git a/x-pack/plugins/rule_registry/tsconfig.json b/x-pack/plugins/rule_registry/tsconfig.json index 71f1e13a199b5..8c244ed95e014 100644 --- a/x-pack/plugins/rule_registry/tsconfig.json +++ b/x-pack/plugins/rule_registry/tsconfig.json @@ -34,7 +34,6 @@ "@kbn/alerts-as-data-utils", "@kbn/core-http-router-server-mocks", "@kbn/core-http-server", - "@kbn/alerting-state-types", "@kbn/alerting-types" ], "exclude": [ diff --git a/x-pack/plugins/search_playground/public/components/summarization_panel/summarization_model.tsx b/x-pack/plugins/search_playground/public/components/summarization_panel/summarization_model.tsx index e13823d87fd8d..54c8713b35336 100644 --- a/x-pack/plugins/search_playground/public/components/summarization_panel/summarization_model.tsx +++ b/x-pack/plugins/search_playground/public/components/summarization_panel/summarization_model.tsx @@ -93,7 +93,9 @@ export const SummarizationModel: React.FC = ({ useEffect(() => { usageTracker?.click( - `${AnalyticsEvents.modelSelected}_${selectedModel!.value || selectedModel!.connectorType}` + `${AnalyticsEvents.modelSelected}_${ + selectedModel?.value || selectedModel?.connectorType || 'unknown' + }` ); }, [usageTracker, selectedModel]); diff --git a/x-pack/plugins/security/public/session/session_expiration_toast.test.tsx b/x-pack/plugins/security/public/session/session_expiration_toast.test.tsx index f2f1f6ff92f79..46b733c535ec4 100644 --- a/x-pack/plugins/security/public/session/session_expiration_toast.test.tsx +++ b/x-pack/plugins/security/public/session/session_expiration_toast.test.tsx @@ -40,10 +40,25 @@ describe('createSessionExpirationToast', () => { }); describe('SessionExpirationToast', () => { - it('renders session expiration time', () => { + it('renders session expiration time in minutes when >= 60s remaining', () => { const sessionState$ = of({ lastExtensionTime: Date.now(), - expiresInMs: 60 * 1000, + expiresInMs: 60 * 2000, + canBeExtended: true, + }); + + const { getByText } = render( + + + + ); + getByText(/You will be logged out in [0-9]+ minutes/); + }); + + it('renders session expiration time in seconds when < 60s remaining', () => { + const sessionState$ = of({ + lastExtensionTime: Date.now(), + expiresInMs: 60 * 900, canBeExtended: true, }); diff --git a/x-pack/plugins/security/public/session/session_expiration_toast.tsx b/x-pack/plugins/security/public/session/session_expiration_toast.tsx index de0c460f0f3e1..f38638a77bc33 100644 --- a/x-pack/plugins/security/public/session/session_expiration_toast.tsx +++ b/x-pack/plugins/security/public/session/session_expiration_toast.tsx @@ -44,7 +44,7 @@ export const SessionExpirationToast: FunctionComponent, + timeout: , }} /> ); diff --git a/x-pack/plugins/security/server/routes/authorization/roles/get_all_by_space.test.ts b/x-pack/plugins/security/server/routes/authorization/roles/get_all_by_space.test.ts index 06d6d396ce022..956ced4309304 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/get_all_by_space.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/get_all_by_space.test.ts @@ -149,7 +149,7 @@ describe('GET all roles by space id', () => { const paramsSchema = (config.validate as any).params; - expect(config.options).toEqual({ tags: ['access:manage_spaces'] }); + expect(config.security?.authz).toEqual({ requiredPrivileges: ['manage_spaces'] }); expect(() => paramsSchema.validate({})).toThrowErrorMatchingInlineSnapshot( `"[spaceId]: expected value of type [string] but got [undefined]"` ); diff --git a/x-pack/plugins/security/server/routes/authorization/roles/get_all_by_space.ts b/x-pack/plugins/security/server/routes/authorization/roles/get_all_by_space.ts index 734f0292db116..a441ba15164c1 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/get_all_by_space.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/get_all_by_space.ts @@ -24,8 +24,10 @@ export function defineGetAllRolesBySpaceRoutes({ router.get( { path: '/internal/security/roles/{spaceId}', - options: { - tags: ['access:manage_spaces'], + security: { + authz: { + requiredPrivileges: ['manage_spaces'], + }, }, validate: { params: schema.object({ spaceId: schema.string({ minLength: 1 }) }), diff --git a/x-pack/plugins/security/server/routes/session_management/invalidate.test.ts b/x-pack/plugins/security/server/routes/session_management/invalidate.test.ts index fbc14015d80c1..12c31be12dd57 100644 --- a/x-pack/plugins/security/server/routes/session_management/invalidate.test.ts +++ b/x-pack/plugins/security/server/routes/session_management/invalidate.test.ts @@ -46,9 +46,10 @@ describe('Invalidate sessions routes', () => { expect(routeConfig.options).toEqual({ access: 'public', summary: 'Invalidate user sessions', - tags: ['access:sessionManagement'], }); + expect(routeConfig.security?.authz).toEqual({ requiredPrivileges: ['sessionManagement'] }); + const bodySchema = (routeConfig.validate as any).body as ObjectType; expect(() => bodySchema.validate({})).toThrowErrorMatchingInlineSnapshot( `"[match]: expected at least one defined value but got [undefined]"` diff --git a/x-pack/plugins/security/server/routes/session_management/invalidate.ts b/x-pack/plugins/security/server/routes/session_management/invalidate.ts index a45d8f00c1ca4..bbc81c21706d9 100644 --- a/x-pack/plugins/security/server/routes/session_management/invalidate.ts +++ b/x-pack/plugins/security/server/routes/session_management/invalidate.ts @@ -37,6 +37,11 @@ export function defineInvalidateSessionsRoutes({ ), }), }, + security: { + authz: { + requiredPrivileges: ['sessionManagement'], + }, + }, options: { // The invalidate session API was introduced to address situations where the session index // could grow rapidly - when session timeouts are disabled, or with anonymous access. @@ -44,7 +49,7 @@ export function defineInvalidateSessionsRoutes({ // anonymous access. However, keeping this endpoint available internally in serverless would // be useful in situations where we need to batch-invalidate user sessions. access: buildFlavor === 'serverless' ? 'internal' : 'public', - tags: ['access:sessionManagement'], + summary: `Invalidate user sessions`, }, }, diff --git a/x-pack/plugins/security/server/routes/user_profile/bulk_get.test.ts b/x-pack/plugins/security/server/routes/user_profile/bulk_get.test.ts index f5d449bd8423d..eece6b58f8f01 100644 --- a/x-pack/plugins/security/server/routes/user_profile/bulk_get.test.ts +++ b/x-pack/plugins/security/server/routes/user_profile/bulk_get.test.ts @@ -51,7 +51,7 @@ describe('Bulk get profile routes', () => { }); it('correctly defines route.', () => { - expect(routeConfig.options).toEqual({ tags: ['access:bulkGetUserProfiles'] }); + expect(routeConfig.security?.authz).toEqual({ requiredPrivileges: ['bulkGetUserProfiles'] }); const bodySchema = (routeConfig.validate as any).body as ObjectType; expect(() => bodySchema.validate(0)).toThrowErrorMatchingInlineSnapshot( diff --git a/x-pack/plugins/security/server/routes/user_profile/bulk_get.ts b/x-pack/plugins/security/server/routes/user_profile/bulk_get.ts index 20da1d573901f..0ffe760d57d52 100644 --- a/x-pack/plugins/security/server/routes/user_profile/bulk_get.ts +++ b/x-pack/plugins/security/server/routes/user_profile/bulk_get.ts @@ -24,7 +24,11 @@ export function defineBulkGetUserProfilesRoute({ dataPath: schema.maybe(schema.string()), }), }, - options: { tags: ['access:bulkGetUserProfiles'] }, + security: { + authz: { + requiredPrivileges: ['bulkGetUserProfiles'], + }, + }, }, createLicensedRouteHandler(async (context, request, response) => { const userProfileServiceInternal = getUserProfileService(); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/common.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/common.gen.ts index 2dd83ca89bee0..228bf1e515675 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/common.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/common.gen.ts @@ -36,6 +36,7 @@ export const EngineDescriptor = z.object({ status: EngineStatus, filter: z.string().optional(), fieldHistoryLength: z.number().int(), + error: z.object({}).optional(), }); export type InspectQuery = z.infer; diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/common.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/common.schema.yaml index 810961392aad1..00b100516b76c 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/common.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/common.schema.yaml @@ -30,6 +30,8 @@ components: type: string fieldHistoryLength: type: integer + error: + type: object EngineStatus: type: string diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 68aaf7bf9cf04..137afe7ba9112 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -424,7 +424,6 @@ export const RULES_TABLE_MAX_PAGE_SIZE = 100; export const NEW_FEATURES_TOUR_STORAGE_KEYS = { RULE_MANAGEMENT_PAGE: 'securitySolution.rulesManagementPage.newFeaturesTour.v8.13', TIMELINES: 'securitySolution.security.timelineFlyoutHeader.saveTimelineTour', - FLYOUT: 'securitySolution.documentDetails.newFeaturesTour.v8.14', }; export const RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY = diff --git a/x-pack/plugins/security_solution/common/endpoint/constants.ts b/x-pack/plugins/security_solution/common/endpoint/constants.ts index 534d7e5c2b8a4..19aa53eca6649 100644 --- a/x-pack/plugins/security_solution/common/endpoint/constants.ts +++ b/x-pack/plugins/security_solution/common/endpoint/constants.ts @@ -55,6 +55,9 @@ export const telemetryIndexPattern = 'metrics-endpoint.telemetry-*'; export const ENDPOINT_HEARTBEAT_INDEX = '.logs-endpoint.heartbeat-default'; export const ENDPOINT_HEARTBEAT_INDEX_PATTERN = '.logs-endpoint.heartbeat-*'; +// Endpoint diagnostics index +export const DEFAULT_DIAGNOSTIC_INDEX_PATTERN = '.logs-endpoint.diagnostic.collection-*' as const; + // File storage indexes supporting endpoint Upload/download export const FILE_STORAGE_METADATA_INDEX = getFileMetadataIndexName('endpoint'); export const FILE_STORAGE_DATA_INDEX = getFileDataIndexName('endpoint'); diff --git a/x-pack/plugins/security_solution/common/endpoint/utils/index_name_utilities.test.ts b/x-pack/plugins/security_solution/common/endpoint/utils/index_name_utilities.test.ts new file mode 100644 index 0000000000000..25eb35bf88872 --- /dev/null +++ b/x-pack/plugins/security_solution/common/endpoint/utils/index_name_utilities.test.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 { buildIndexNameWithNamespace } from './index_name_utilities'; + +describe('index name utilities', () => { + describe('buildIndexNameWithNamespace()', () => { + test.each(['logs-endpoint.foo', 'logs-endpoint.foo-', 'logs-endpoint.foo-*'])( + `should build correct index name for: %s`, + (prefix) => { + expect(buildIndexNameWithNamespace(prefix, 'bar')).toEqual('logs-endpoint.foo-bar'); + } + ); + }); +}); diff --git a/x-pack/plugins/security_solution/common/endpoint/utils/index_name_utilities.ts b/x-pack/plugins/security_solution/common/endpoint/utils/index_name_utilities.ts new file mode 100644 index 0000000000000..80db5392f653f --- /dev/null +++ b/x-pack/plugins/security_solution/common/endpoint/utils/index_name_utilities.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. + */ + +/** + * Builds an index name that includes the `namespace` using the provided index name prefix or pattern. + * + * @param indexNamePrefixOrPattern + * @param namespace + * + * @example + * + * buildIndexNameWithNamespace('logs-foo.bar-*', 'default'); // == 'logs-foo.bar-default' + * buildIndexNameWithNamespace('logs-foo.bar', 'default'); // == 'logs-foo.bar-default' + * buildIndexNameWithNamespace('logs-foo.bar-', 'default'); // == 'logs-foo.bar-default' + */ +export const buildIndexNameWithNamespace = ( + indexNamePrefixOrPattern: string, + namespace: string +): string => { + if (indexNamePrefixOrPattern.endsWith('*')) { + const hasDash = indexNamePrefixOrPattern.endsWith('-*'); + return `${indexNamePrefixOrPattern.substring(0, indexNamePrefixOrPattern.length - 1)}${ + hasDash ? '' : '-' + }${namespace}`; + } + + return `${indexNamePrefixOrPattern}${ + indexNamePrefixOrPattern.endsWith('-') ? '' : '-' + }${namespace}`; +}; diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index 792b6352912b3..892b0a0226639 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -94,9 +94,9 @@ export const allowedExperimentalValues = Object.freeze({ endpointManagementSpaceAwarenessEnabled: false, /** - * Enables new notes + * Disables new notes */ - securitySolutionNotesEnabled: false, + securitySolutionNotesDisabled: false, /** * Disables entity and alert previews @@ -111,7 +111,7 @@ export const allowedExperimentalValues = Object.freeze({ /** * Enables new Knowledge Base Entries features, introduced in `8.15.0`. */ - assistantKnowledgeBaseByDefault: false, + assistantKnowledgeBaseByDefault: true, /** * Enables the Managed User section inside the new user details flyout. diff --git a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml index fef2850b44c90..60bd38c246f34 100644 --- a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml @@ -785,6 +785,8 @@ components: EngineDescriptor: type: object properties: + error: + type: object fieldHistoryLength: type: integer filter: diff --git a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml index 3a5054f17a460..fc63924118968 100644 --- a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml @@ -785,6 +785,8 @@ components: EngineDescriptor: type: object properties: + error: + type: object fieldHistoryLength: type: integer filter: diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/misconfiguration/misconfiguration_preview.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/misconfiguration/misconfiguration_preview.tsx index 091179b40393c..b133e9db22050 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/misconfiguration/misconfiguration_preview.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/misconfiguration/misconfiguration_preview.tsx @@ -13,7 +13,6 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { DistributionBar } from '@kbn/security-solution-distribution-bar'; import { useMisconfigurationPreview } from '@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview'; import { i18n } from '@kbn/i18n'; -import { ExpandablePanel } from '@kbn/security-solution-common'; import { buildEntityFlyoutPreviewQuery } from '@kbn/cloud-security-posture-common'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; import { useVulnerabilitiesPreview } from '@kbn/cloud-security-posture/src/hooks/use_vulnerabilities_preview'; @@ -23,6 +22,7 @@ import { ENTITY_FLYOUT_WITH_MISCONFIGURATION_VISIT, uiMetricService, } from '@kbn/cloud-security-posture-common/utils/ui_metrics'; +import { ExpandablePanel } from '../../../flyout/shared/components/expandable_panel'; import { CspInsightLeftPanelSubTab, EntityDetailsLeftPanelTab, diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/vulnerabilities/vulnerabilities_preview.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/vulnerabilities/vulnerabilities_preview.tsx index 2c162ba9db894..1caa740662ad8 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/vulnerabilities/vulnerabilities_preview.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/vulnerabilities/vulnerabilities_preview.tsx @@ -12,7 +12,6 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, useEuiTheme, EuiTitle } import { FormattedMessage } from '@kbn/i18n-react'; import { DistributionBar } from '@kbn/security-solution-distribution-bar'; import { useVulnerabilitiesPreview } from '@kbn/cloud-security-posture/src/hooks/use_vulnerabilities_preview'; -import { ExpandablePanel } from '@kbn/security-solution-common'; import { buildEntityFlyoutPreviewQuery, getAbbreviatedNumber, @@ -25,6 +24,7 @@ import { uiMetricService, } from '@kbn/cloud-security-posture-common/utils/ui_metrics'; import { METRIC_TYPE } from '@kbn/analytics'; +import { ExpandablePanel } from '../../../flyout/shared/components/expandable_panel'; import { EntityDetailsLeftPanelTab } from '../../../flyout/entity_details/shared/components/left_panel/left_panel_header'; import { HostDetailsPanelKey } from '../../../flyout/entity_details/host_details_left'; import { useRiskScore } from '../../../entity_analytics/api/hooks/use_risk_score'; 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 931c519ae9b57..5ec3e0c2d0e3d 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 @@ -93,8 +93,8 @@ const RowActionComponent = ({ [columnHeaders, timelineNonEcsData] ); - const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled( - 'securitySolutionNotesEnabled' + const securitySolutionNotesDisabled = useIsExperimentalFeatureEnabled( + 'securitySolutionNotesDisabled' ); const handleOnEventDetailPanelOpened = useCallback(() => { @@ -175,12 +175,12 @@ const RowActionComponent = ({ showCheckboxes={showCheckboxes} tabType={tabType} timelineId={tableId} - toggleShowNotes={securitySolutionNotesEnabled ? toggleShowNotes : undefined} + toggleShowNotes={securitySolutionNotesDisabled ? undefined : toggleShowNotes} width={width} setEventsLoading={setEventsLoading} setEventsDeleted={setEventsDeleted} refetch={refetch} - showNotes={securitySolutionNotesEnabled ? true : false} + showNotes={!securitySolutionNotesDisabled} /> )} diff --git a/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.test.tsx b/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.test.tsx index dd5cbb4131b4c..0a5156bddcc99 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.test.tsx @@ -16,6 +16,9 @@ import { useGlobalFullScreen } from '../../containers/use_full_screen'; import { licenseService } from '../../hooks/use_license'; import { mockHistory } from '../../mock/router'; import { DEFAULT_EVENTS_STACK_BY_VALUE } from './histogram_configurations'; +import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features'; + +jest.mock('../../hooks/use_experimental_features'); const mockGetDefaultControlColumn = jest.fn(); jest.mock('../../../timelines/components/timeline/body/control_columns', () => ({ @@ -95,6 +98,7 @@ describe('EventsQueryTabBody', () => { }; beforeEach(() => { + (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false); jest.clearAllMocks(); }); @@ -106,7 +110,7 @@ describe('EventsQueryTabBody', () => { ); expect(queryByText('MockedStatefulEventsViewer')).toBeInTheDocument(); - expect(mockGetDefaultControlColumn).toHaveBeenCalledWith(4); + expect(mockGetDefaultControlColumn).toHaveBeenCalledWith(5); }); it('renders the matrix histogram when globalFullScreen is false', () => { @@ -186,7 +190,19 @@ describe('EventsQueryTabBody', () => { expect(spy).toHaveBeenCalled(); }); - it('only have 4 columns on Action bar for non-Enterprise user', () => { + it('should have 5 columns on Action bar for non-Enterprise user', () => { + render( + + + + ); + + expect(mockGetDefaultControlColumn).toHaveBeenCalledWith(5); + }); + + it('should have 4 columns on Action bar for non-Enterprise user and securitySolutionNotesDisabled is true', () => { + (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true); + render( @@ -196,10 +212,23 @@ describe('EventsQueryTabBody', () => { expect(mockGetDefaultControlColumn).toHaveBeenCalledWith(4); }); - it('shows 5 columns on Action bar for Enterprise user', () => { + it('should 6 columns on Action bar for Enterprise user', () => { const licenseServiceMock = licenseService as jest.Mocked; + licenseServiceMock.isEnterprise.mockReturnValue(true); + render( + + + + ); + + expect(mockGetDefaultControlColumn).toHaveBeenCalledWith(6); + }); + + it('should 6 columns on Action bar for Enterprise user and securitySolutionNotesDisabled is true', () => { + const licenseServiceMock = licenseService as jest.Mocked; licenseServiceMock.isEnterprise.mockReturnValue(true); + (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true); render( diff --git a/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.tsx b/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.tsx index 92c94549ca891..70e56f4a8b4d2 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.tsx @@ -75,10 +75,10 @@ const EventsQueryTabBodyComponent: React.FC = const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); const isEnterprisePlus = useLicense().isEnterprise(); let ACTION_BUTTON_COUNT = isEnterprisePlus ? 6 : 5; - const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled( - 'securitySolutionNotesEnabled' + const securitySolutionNotesDisabled = useIsExperimentalFeatureEnabled( + 'securitySolutionNotesDisabled' ); - if (!securitySolutionNotesEnabled) { + if (securitySolutionNotesDisabled) { ACTION_BUTTON_COUNT--; } const leadingControlColumns = useMemo( diff --git a/x-pack/plugins/security_solution/public/common/components/header_actions/actions.tsx b/x-pack/plugins/security_solution/public/common/components/header_actions/actions.tsx index 2f27b1c8d8d9f..4775b8f889028 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_actions/actions.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_actions/actions.tsx @@ -255,8 +255,8 @@ const ActionsComponent: React.FC = ({ onEventDetailsPanelOpened(); }, [activeStep, incrementStep, isTourAnchor, isTourShown, onEventDetailsPanelOpened]); - const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled( - 'securitySolutionNotesEnabled' + const securitySolutionNotesDisabled = useIsExperimentalFeatureEnabled( + 'securitySolutionNotesDisabled' ); /* only applicable for new event based notes */ @@ -270,7 +270,7 @@ const ActionsComponent: React.FC = ({ /* note ids associated with the document AND attached to the current timeline, used for pinning */ const timelineNoteIds = useMemo(() => { - if (securitySolutionNotesEnabled) { + if (!securitySolutionNotesDisabled) { // if timeline is unsaved, there is no notes associated to timeline yet return savedObjectId ? documentBasedNotesInTimeline.map((note) => note.noteId) : []; } @@ -280,13 +280,13 @@ const ActionsComponent: React.FC = ({ eventId, documentBasedNotesInTimeline, savedObjectId, - securitySolutionNotesEnabled, + securitySolutionNotesDisabled, ]); /* note count of the document */ const notesCount = useMemo( - () => (securitySolutionNotesEnabled ? documentBasedNotes.length : timelineNoteIds.length), - [documentBasedNotes, timelineNoteIds, securitySolutionNotesEnabled] + () => (securitySolutionNotesDisabled ? timelineNoteIds.length : documentBasedNotes.length), + [documentBasedNotes, timelineNoteIds, securitySolutionNotesDisabled] ); // we hide the analyzer icon if the data is not available for the resolver diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_embeddable.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_embeddable.tsx index a72675d381404..0461cb8888be5 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_embeddable.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_embeddable.tsx @@ -34,24 +34,26 @@ import { useEmbeddableInspect } from './use_embeddable_inspect'; import { useVisualizationResponse } from './use_visualization_response'; import { useInspect } from '../inspect/use_inspect'; -const HOVER_ACTIONS_PADDING = 24; const DISABLED_ACTIONS = ['ACTION_CUSTOMIZE_PANEL']; const LensComponentWrapper = styled.div<{ $height?: number; width?: string | number; - $addHoverActionsPadding?: boolean; }>` height: ${({ $height }) => ($height ? `${$height}px` : 'auto')}; width: ${({ width }) => width ?? 'auto'}; - ${({ $addHoverActionsPadding }) => - $addHoverActionsPadding ? `.embPanel__header { top: ${HOVER_ACTIONS_PADDING * -1}px; }` : ''} + .embPanel { + outline: none; + } + + .embPanel__hoverActions.embPanel__hoverActionsRight { + border-radius: 6px !important; + border-bottom: 1px solid #d3dae6 !important; + } - .embPanel__header { - z-index: 2; - position: absolute; - right: 0; + .embPanel__hoverActionsAnchor .embPanel__hoverActionsWrapper { + top: -20px; } .expExpressionRenderer__expression { @@ -110,10 +112,7 @@ const LensEmbeddableComponent: React.FC = ({ title: '', }); const preferredSeriesType = (attributes?.state?.visualization as XYState)?.preferredSeriesType; - // Avoid hover actions button overlaps with its chart - const addHoverActionsPadding = - attributes?.visualizationType !== 'lnsLegacyMetric' && - attributes?.visualizationType !== 'lnsPie'; + const LensComponent = lens.EmbeddableComponent; const overrides: TypedLensByValueInput['overrides'] = useMemo( @@ -255,11 +254,7 @@ const LensEmbeddableComponent: React.FC = ({ return ( <> {attributes && searchSessionId && ( - + { describe('computeHasMetadataOperator', () => { it('should be false if query does not have operator', () => { expect(computeHasMetadataOperator(getQeryAst('from test*'))).toBe(false); - expect(computeHasMetadataOperator(getQeryAst('from test* [metadata]'))).toBe(false); - expect(computeHasMetadataOperator(getQeryAst('from test* [metadata id]'))).toBe(false); + expect(computeHasMetadataOperator(getQeryAst('from test* metadata'))).toBe(false); + expect(computeHasMetadataOperator(getQeryAst('from test* metadata id'))).toBe(false); expect(computeHasMetadataOperator(getQeryAst('from metadata*'))).toBe(false); expect(computeHasMetadataOperator(getQeryAst('from test* | keep metadata'))).toBe(false); - expect(computeHasMetadataOperator(getQeryAst('from test* | eval x="[metadata _id]"'))).toBe( + expect(computeHasMetadataOperator(getQeryAst('from test* | eval x="metadata _id"'))).toBe( false ); }); @@ -48,19 +48,19 @@ describe('computeHasMetadataOperator', () => { ).toBe(true); // still validates deprecated square bracket syntax - expect(computeHasMetadataOperator(getQeryAst('from test* [metadata _id]'))).toBe(true); - expect(computeHasMetadataOperator(getQeryAst('from test* [metadata _id, _index]'))).toBe(true); - expect(computeHasMetadataOperator(getQeryAst('from test* [metadata _index, _id]'))).toBe(true); - expect(computeHasMetadataOperator(getQeryAst('from test* [ metadata _id ]'))).toBe(true); - expect(computeHasMetadataOperator(getQeryAst('from test* [ metadata _id] '))).toBe(true); - expect(computeHasMetadataOperator(getQeryAst('from test* [ metadata _id] | limit 10'))).toBe( + expect(computeHasMetadataOperator(getQeryAst('from test* metadata _id'))).toBe(true); + expect(computeHasMetadataOperator(getQeryAst('from test* metadata _id, _index'))).toBe(true); + expect(computeHasMetadataOperator(getQeryAst('from test* metadata _index, _id'))).toBe(true); + expect(computeHasMetadataOperator(getQeryAst('from test* metadata _id '))).toBe(true); + expect(computeHasMetadataOperator(getQeryAst('from test* metadata _id '))).toBe(true); + expect(computeHasMetadataOperator(getQeryAst('from test* metadata _id | limit 10'))).toBe( true ); expect( computeHasMetadataOperator( - getQeryAst(`from packetbeat* [metadata + getQeryAst(`from packetbeat* metadata - _id ] + _id | limit 100`) ) ).toBe(true); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.ts index 869e379c21aed..c508676cae92c 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.ts @@ -154,7 +154,7 @@ export const parseEsqlQuery = (query: string) => { return { errors, isEsqlQueryAggregating, - // non-aggregating query which does not have [metadata], is not a valid one + // non-aggregating query which does not have metadata, is not a valid one isMissingMetadataOperator: !isEsqlQueryAggregating && !computeHasMetadataOperator(ast), }; }; diff --git a/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_actions_column.tsx b/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_actions_column.tsx index 39b8d7cad3f60..286d377d86f56 100644 --- a/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_actions_column.tsx +++ b/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_actions_column.tsx @@ -47,10 +47,10 @@ export const getUseActionColumnHook = } // we only want to show the note icon if the new notes system feature flag is enabled - const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled( - 'securitySolutionNotesEnabled' + const securitySolutionNotesDisabled = useIsExperimentalFeatureEnabled( + 'securitySolutionNotesDisabled' ); - if (!securitySolutionNotesEnabled) { + if (securitySolutionNotesDisabled) { ACTION_BUTTON_COUNT--; } diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/components/dashboard_panels.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/components/dashboard_panels.tsx index 3b4f661e949f2..d70eb9fe34b51 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/components/dashboard_panels.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/components/dashboard_panels.tsx @@ -15,6 +15,7 @@ import { EuiLoadingLogo, EuiPanel, EuiImage, + EuiCallOut, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -50,9 +51,25 @@ const EntityStoreDashboardPanelsComponent = () => { const entityStore = useEntityEngineStatus(); const riskEngineStatus = useRiskEngineStatus(); - const { enable: enableStore } = useEntityStoreEnablement(); + const { enable: enableStore, query } = useEntityStoreEnablement(); + const { mutate: initRiskEngine } = useInitRiskEngineMutation(); + const callouts = entityStore.errors.map((err) => ( + + } + color="danger" + iconType="error" + > +

{err?.message}

+
+ )); + const enableEntityStore = (enable: Enablements) => () => { setModalState({ visible: false }); if (enable.riskScore) { @@ -66,6 +83,7 @@ const EntityStoreDashboardPanelsComponent = () => { }; setRiskEngineInitializing(true); initRiskEngine(undefined, options); + return; } if (enable.entityStore) { @@ -73,6 +91,26 @@ const EntityStoreDashboardPanelsComponent = () => { } }; + if (query.error) { + return ( + <> + + } + color="danger" + iconType="error" + > +

{(query.error as { body: { message: string } }).body.message}

+
+ {callouts} + + ); + } + if (entityStore.status === 'loading') { return ( @@ -103,27 +141,27 @@ const EntityStoreDashboardPanelsComponent = () => { ); } + // TODO Rename variable because the Risk score could be installed but disabled const isRiskScoreAvailable = riskEngineStatus.data && riskEngineStatus.data.risk_engine_status !== RiskEngineStatusEnum.NOT_INSTALLED; return ( - {entityStore.status === 'enabled' && isRiskScoreAvailable && ( + {entityStore.status === 'error' && isRiskScoreAvailable && ( <> + {callouts} - - - )} - {entityStore.status === 'enabled' && !isRiskScoreAvailable && ( + {entityStore.status === 'error' && !isRiskScoreAvailable && ( <> + {callouts} setModalState({ visible: true })} @@ -131,43 +169,69 @@ const EntityStoreDashboardPanelsComponent = () => { enablements="riskScore" /> - + + )} + {entityStore.status === 'enabled' && isRiskScoreAvailable && ( + <> + + + + + + )} - - {entityStore.status === 'not_installed' && !isRiskScoreAvailable && ( - // TODO: Move modal inside EnableEntityStore component, eliminating the onEnable prop in favour of forwarding the riskScoreEnabled status - setModalState({ visible: true })} - loadingRiskEngine={riskEngineInitializing} - /> - )} - - {entityStore.status === 'not_installed' && isRiskScoreAvailable && ( + {entityStore.status === 'enabled' && !isRiskScoreAvailable && ( <> - setModalState({ - visible: true, - }) - } + onEnable={() => setModalState({ visible: true })} + loadingRiskEngine={riskEngineInitializing} + enablements="riskScore" /> + - - - - + )} + {(entityStore.status === 'not_installed' || entityStore.status === 'stopped') && + !isRiskScoreAvailable && ( + // TODO: Move modal inside EnableEntityStore component, eliminating the onEnable prop in favour of forwarding the riskScoreEnabled status + setModalState({ visible: true })} + loadingRiskEngine={riskEngineInitializing} + /> + )} + + {(entityStore.status === 'not_installed' || entityStore.status === 'stopped') && + isRiskScoreAvailable && ( + <> + + + setModalState({ + visible: true, + }) + } + /> + + + + + + + + + )} + setModalState({ visible })} diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/entities_list.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/entities_list.test.tsx index 0e598d6463c5a..91f0c42eab385 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/entities_list.test.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/entities_list.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { render, screen, fireEvent } from '@testing-library/react'; +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { EntitiesList } from './entities_list'; import { useGlobalTime } from '../../../common/containers/use_global_time'; import { useQueryToggle } from '../../../common/containers/query_toggle'; @@ -15,6 +15,7 @@ import { useErrorToast } from '../../../common/hooks/use_error_toast'; import type { ListEntitiesResponse } from '../../../../common/api/entity_analytics/entity_store/entities/list_entities.gen'; import { useGlobalFilterQuery } from '../../../common/hooks/use_global_filter_query'; import { TestProviders } from '../../../common/mock'; +import { times } from 'lodash/fp'; jest.mock('../../../common/containers/use_global_time'); jest.mock('../../../common/containers/query_toggle'); @@ -22,21 +23,23 @@ jest.mock('./hooks/use_entities_list_query'); jest.mock('../../../common/hooks/use_error_toast'); jest.mock('../../../common/hooks/use_global_filter_query'); -const entityName = 'Entity Name'; +const secondPageTestId = 'pagination-button-1'; +const entityName = 'Entity Name 1'; const responseData: ListEntitiesResponse = { page: 1, per_page: 10, - total: 1, - records: [ - { + total: 20, + records: times( + (index) => ({ '@timestamp': '2021-08-02T14:00:00.000Z', - user: { name: entityName }, + user: { name: `Entity Name ${index}` }, entity: { - name: entityName, + name: `Entity Name ${index}`, source: 'test-index', }, - }, - ], + }), + 10 + ), inspect: undefined, }; @@ -81,7 +84,7 @@ describe('EntitiesList', () => { it('displays the correct number of rows', () => { render(, { wrapper: TestProviders }); - expect(screen.getAllByRole('row')).toHaveLength(2); + expect(screen.getAllByRole('row')).toHaveLength(10 + 1); }); it('calls refetch on time range change', () => { @@ -112,6 +115,21 @@ describe('EntitiesList', () => { ); }); + it('should reset the page when sort order changes ', async () => { + render(, { wrapper: TestProviders }); + + const secondPageButton = screen.getByTestId(secondPageTestId); + fireEvent.click(secondPageButton); + + const columnHeader = screen.getByText('Name'); + fireEvent.click(columnHeader); + + await waitFor(() => { + const firstPageButton = screen.getByTestId('pagination-button-0'); + expect(firstPageButton).toHaveAttribute('aria-current', 'true'); + }); + }); + it('displays error toast when there is an error', () => { const error = new Error('Test error'); mockUseEntitiesListQuery.mockReturnValueOnce({ diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/entities_list.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/entities_list.tsx index aa03e41c553cb..69afa8dd32108 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/entities_list.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/entities_list.tsx @@ -94,6 +94,11 @@ export const EntitiesList: React.FC = () => { inspect: data?.inspect ?? null, }); + // Reset the active page when the search criteria changes + useEffect(() => { + setActivePage(0); + }, [sorting, limit, filter]); + const columns = useEntitiesListColumns(); // Force a refetch when "refresh" button is clicked. @@ -112,7 +117,7 @@ export const EntitiesList: React.FC = () => { return ( ['refetchInterval']; } +interface EngineError { + message: string; +} + export const useEntityEngineStatus = (opts: Options = {}) => { // QUESTION: Maybe we should have an `EnablementStatus` API route for this? const { listEntityEngines } = useEntityStoreRoutes(); @@ -33,6 +37,10 @@ export const useEntityEngineStatus = (opts: Options = {}) => { return 'not_installed'; } + if (data?.engines?.some((engine) => engine.status === 'error')) { + return 'error'; + } + if (data?.engines?.every((engine) => engine.status === 'stopped')) { return 'stopped'; } @@ -52,7 +60,12 @@ export const useEntityEngineStatus = (opts: Options = {}) => { return 'enabled'; })(); + const errors = (data?.engines + ?.filter((engine) => engine.status === 'error') + .map((engine) => engine.error) ?? []) as EngineError[]; + return { status, + errors, }; }; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entity_store.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entity_store.ts index 2d9fa716faf1c..21e73241451e5 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entity_store.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entity_store.ts @@ -41,9 +41,10 @@ export const useEntityStoreEnablement = () => { }); const { initEntityStore } = useEntityStoreRoutes(); - const { refetch: initialize } = useQuery({ + const { refetch: initialize, ...query } = useQuery({ queryKey: [ENTITY_STORE_ENABLEMENT_INIT], - queryFn: () => Promise.all([initEntityStore('user'), initEntityStore('host')]), + queryFn: async () => + initEntityStore('user').then((usr) => initEntityStore('host').then((host) => [usr, host])), enabled: false, }); @@ -51,10 +52,10 @@ export const useEntityStoreEnablement = () => { telemetry?.reportEntityStoreInit({ timestamp: new Date().toISOString(), }); - initialize().then(() => setPolling(true)); + return initialize().then(() => setPolling(true)); }, [initialize, telemetry]); - return { enable }; + return { enable, query }; }; export const INIT_ENTITY_ENGINE_STATUS_KEY = ['POST', 'INIT_ENTITY_ENGINE']; @@ -79,7 +80,9 @@ export const useInitEntityEngineMutation = (options?: UseMutationOptions<{}>) => timestamp: new Date().toISOString(), action: 'start', }); - return Promise.all([initEntityStore('user'), initEntityStore('host')]); + return initEntityStore('user').then((usr) => + initEntityStore('host').then((host) => [usr, host]) + ); }, { ...options, @@ -107,7 +110,9 @@ export const useStopEntityEngineMutation = (options?: UseMutationOptions<{}>) => timestamp: new Date().toISOString(), action: 'stop', }); - return Promise.all([stopEntityStore('user'), stopEntityStore('host')]); + return stopEntityStore('user').then((usr) => + stopEntityStore('host').then((host) => [usr, host]) + ); }, { ...options, @@ -129,7 +134,10 @@ export const useDeleteEntityEngineMutation = (options?: UseMutationOptions<{}>) const invalidateEntityEngineStatusQuery = useInvalidateEntityEngineStatusQuery(); const { deleteEntityEngine } = useEntityStoreRoutes(); return useMutation( - () => Promise.all([deleteEntityEngine('user', true), deleteEntityEngine('host', true)]), + () => + deleteEntityEngine('user', true).then((usr) => + deleteEntityEngine('host', true).then((host) => [usr, host]) + ), { ...options, mutationKey: DELETE_ENTITY_ENGINE_STATUS_KEY, diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.tsx index fe32fad5b0070..2dec7d07ce6e8 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.tsx @@ -22,16 +22,14 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { euiThemeVars } from '@kbn/ui-theme'; import dateMath from '@kbn/datemath'; import { i18n } from '@kbn/i18n'; -import { ExpandablePanel } from '@kbn/security-solution-common'; import { useKibana } from '../../../common/lib/kibana/kibana_react'; - import { EntityDetailsLeftPanelTab } from '../../../flyout/entity_details/shared/components/left_panel/left_panel_header'; - import { InspectButton, InspectButtonContainer } from '../../../common/components/inspect'; import { ONE_WEEK_IN_HOURS } from '../../../flyout/entity_details/shared/constants'; import { FormattedRelativePreferenceDate } from '../../../common/components/formatted_date'; import { RiskScoreEntity } from '../../../../common/entity_analytics/risk_engine'; import { VisualizationEmbeddable } from '../../../common/components/visualization_actions/visualization_embeddable'; +import { ExpandablePanel } from '../../../flyout/shared/components/expandable_panel'; import type { RiskScoreState } from '../../api/hooks/use_risk_score'; import { getRiskScoreSummaryAttributes } from '../../lens_attributes/risk_score_summary'; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/pages/entity_store_management_page.tsx b/x-pack/plugins/security_solution/public/entity_analytics/pages/entity_store_management_page.tsx index 1ca2b0e2b02da..a6e85a2c1398a 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/pages/entity_store_management_page.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/pages/entity_store_management_page.tsx @@ -252,6 +252,21 @@ export const EntityStoreManagementPage = () => { stopEntityEngineMutation.isLoading || deleteEntityEngineMutation.isLoading; + const callouts = entityStoreStatus.errors.map((error) => ( + + } + color="danger" + iconType="alert" + > +

{error.message}

+
+ )); + return ( <> { + {initEntityEngineMutation.isError && ( + + } + color="danger" + iconType="alert" + > +

+ {(initEntityEngineMutation.error as { body: { message: string } }).body.message} +

+
+ )} + {deleteEntityEngineMutation.isError && ( + + } + color="danger" + iconType="alert" + > +

+ {(deleteEntityEngineMutation.error as { body: { message: string } }).body.message} +

+
+ )} + {callouts} {!isEntityStoreFeatureFlagDisabled && canDeleteEntityEngine && }
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/alert_reason/alert_reason.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/alert_reason/alert_reason.tsx index 03b84bc625552..62e6e9f492b2f 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/alert_reason/alert_reason.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/alert_reason/alert_reason.tsx @@ -10,11 +10,11 @@ import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import styled from '@emotion/styled'; import { euiThemeVars } from '@kbn/ui-theme'; import { FormattedMessage } from '@kbn/i18n-react'; -import { FlyoutError } from '@kbn/security-solution-common'; import { ALERT_REASON_BODY_TEST_ID } from './test_ids'; import { useAlertReasonPanelContext } from './context'; import { getRowRenderer } from '../../../timelines/components/timeline/body/renderers/get_row_renderer'; import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers'; +import { FlyoutError } from '../../shared/components/flyout_error'; const ReasonContainerWrapper = styled.div` overflow-x: auto; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/alert_reason/context.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/alert_reason/context.tsx index ec8096d60e72c..b67c9af508d96 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/alert_reason/context.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/alert_reason/context.tsx @@ -7,8 +7,9 @@ import React, { createContext, memo, useContext, useMemo } from 'react'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; -import { FlyoutError, FlyoutLoading } from '@kbn/security-solution-common'; import { useEventDetails } from '../shared/hooks/use_event_details'; +import { FlyoutError } from '../../shared/components/flyout_error'; +import { FlyoutLoading } from '../../shared/components/flyout_loading'; import type { AlertReasonPanelProps } from '.'; export interface AlertReasonPanelContext { diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/analyzer_panels/index.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/analyzer_panels/index.tsx index f61993da5321a..4fe9086c70a0e 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/analyzer_panels/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/analyzer_panels/index.tsx @@ -7,13 +7,13 @@ import React, { useCallback } from 'react'; import type { FlyoutPanelProps } from '@kbn/expandable-flyout'; -import { FlyoutBody } from '@kbn/security-solution-common'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; import type { DocumentDetailsAnalyzerPanelKey } from '../shared/constants/panel_keys'; import { DetailsPanel } from '../../../resolver/view/details_panel'; import type { NodeEventOnClick } from '../../../resolver/view/panels/node_events_of_type'; import { DocumentDetailsPreviewPanelKey } from '../shared/constants/panel_keys'; import { ALERT_PREVIEW_BANNER, EVENT_PREVIEW_BANNER } from '../preview/constants'; +import { FlyoutBody } from '../../shared/components/flyout_body'; interface AnalyzerPanelProps extends Record { /** diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/isolate_host/content.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/isolate_host/content.tsx index 6ae172ca62556..0c9f05391d82a 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/isolate_host/content.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/isolate_host/content.tsx @@ -8,7 +8,6 @@ import type { FC } from 'react'; import React, { useCallback } from 'react'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import { FlyoutBody } from '@kbn/security-solution-common'; import { DocumentDetailsRightPanelKey } from '../shared/constants/panel_keys'; import { useBasicDataFromDetailsData } from '../shared/hooks/use_basic_data_from_details_data'; import { @@ -17,6 +16,7 @@ import { } from '../../../common/components/endpoint/host_isolation'; import { useHostIsolation } from '../shared/hooks/use_host_isolation'; import { useIsolateHostPanelContext } from './context'; +import { FlyoutBody } from '../../shared/components/flyout_body'; /** * Document details expandable flyout section content for the isolate host component, displaying the form or the success banner diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/isolate_host/context.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/isolate_host/context.tsx index 6abfe1c4e8650..53393e2f8a79b 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/isolate_host/context.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/isolate_host/context.tsx @@ -8,8 +8,9 @@ import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common'; import React, { createContext, memo, useContext, useMemo } from 'react'; -import { FlyoutError, FlyoutLoading } from '@kbn/security-solution-common'; import { useEventDetails } from '../shared/hooks/use_event_details'; +import { FlyoutError } from '../../shared/components/flyout_error'; +import { FlyoutLoading } from '../../shared/components/flyout_loading'; import type { IsolateHostPanelProps } from '.'; export interface IsolateHostPanelContext { diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/isolate_host/header.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/isolate_host/header.tsx index d7effaea8016b..c0f4174cff95a 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/isolate_host/header.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/isolate_host/header.tsx @@ -8,11 +8,11 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui'; import type { FC } from 'react'; import React from 'react'; -import { FlyoutHeader } from '@kbn/security-solution-common'; import { AgentTypeIntegration } from '../../../common/components/endpoint/agents/agent_type_integration'; import { useAlertResponseActionsSupport } from '../../../common/hooks/endpoint/use_alert_response_actions_support'; import { useIsolateHostPanelContext } from './context'; import { FLYOUT_HEADER_TITLE_TEST_ID } from './test_ids'; +import { FlyoutHeader } from '../../shared/components/flyout_header'; import { ISOLATE_HOST, UNISOLATE_HOST } from '../../../common/components/endpoint'; /** diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/correlations_details.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/correlations_details.test.tsx index 859da37c4082d..b3b129a75c13d 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/correlations_details.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/correlations_details.test.tsx @@ -28,7 +28,7 @@ import { useFetchRelatedAlertsBySameSourceEvent } from '../../shared/hooks/use_f import { useFetchRelatedCases } from '../../shared/hooks/use_fetch_related_cases'; import { mockContextValue } from '../../shared/mocks/mock_context'; import { useTimelineDataFilters } from '../../../../timelines/containers/use_timeline_data_filters'; -import { EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID } from '@kbn/security-solution-common'; +import { EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID } from '../../../shared/components/test_ids'; jest.mock('react-router-dom', () => { const actual = jest.requireActual('react-router-dom'); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.tsx index 20663b56dab39..d8497ca984ea8 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.tsx @@ -14,13 +14,13 @@ import { isRight } from 'fp-ts/lib/Either'; import { ALERT_REASON, ALERT_RULE_NAME } from '@kbn/rule-data-utils'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import { ExpandablePanel } from '@kbn/security-solution-common'; import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { CellTooltipWrapper } from '../../shared/components/cell_tooltip_wrapper'; import type { DataProvider } from '../../../../../common/types'; import { SeverityBadge } from '../../../../common/components/severity_badge'; import { usePaginatedAlerts } from '../hooks/use_paginated_alerts'; import { InvestigateInTimelineButton } from '../../../../common/components/event_details/investigate_in_timeline_button'; +import { ExpandablePanel } from '../../../shared/components/expandable_panel'; import { ACTION_INVESTIGATE_IN_TIMELINE } from '../../../../detections/components/alerts_table/translations'; import { getDataProvider } from '../../../../common/components/event_details/use_action_cell_data_provider'; import { AlertPreviewButton } from '../../../shared/components/alert_preview_button'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/entities_details.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/entities_details.test.tsx index 53936a5ed2e99..f79dfcec7b27b 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/entities_details.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/entities_details.test.tsx @@ -13,7 +13,7 @@ import { TestProviders } from '../../../../common/mock'; import { EntitiesDetails } from './entities_details'; import { ENTITIES_DETAILS_TEST_ID, HOST_DETAILS_TEST_ID, USER_DETAILS_TEST_ID } from './test_ids'; import { mockContextValue } from '../../shared/mocks/mock_context'; -import { EXPANDABLE_PANEL_CONTENT_TEST_ID } from '@kbn/security-solution-common'; +import { EXPANDABLE_PANEL_CONTENT_TEST_ID } from '../../../shared/components/test_ids'; import type { Anomalies } from '../../../../common/components/ml/types'; import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities'; import { mockAnomalies } from '../../../../common/components/ml/mock'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.test.tsx index 895ff3d1b7697..4eeabe67979a5 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.test.tsx @@ -30,7 +30,7 @@ import { HOST_DETAILS_VULNERABILITIES_TEST_ID, HOST_DETAILS_ALERT_COUNT_TEST_ID, } from './test_ids'; -import { EXPANDABLE_PANEL_CONTENT_TEST_ID } from '@kbn/security-solution-common'; +import { EXPANDABLE_PANEL_CONTENT_TEST_ID } from '../../../shared/components/test_ids'; import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score'; import { mockContextValue } from '../../shared/mocks/mock_context'; import { mockFlyoutApi } from '../../shared/mocks/mock_flyout_context'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.tsx index 122caa657b039..f5a1e112afa80 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.tsx @@ -25,8 +25,8 @@ import type { EuiBasicTableColumn } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import { ExpandablePanel } from '@kbn/security-solution-common'; import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; +import { ExpandablePanel } from '../../../shared/components/expandable_panel'; import type { RelatedUser } from '../../../../../common/search_strategy/security_solution/related_entities/related_users'; import type { RiskSeverity } from '../../../../../common/search_strategy'; import { HostOverview } from '../../../../overview/components/host_overview'; 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 f39057a16bfdb..ee1bebdb336ce 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 @@ -7,11 +7,11 @@ import React from 'react'; import { EuiLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { FlyoutLoading } from '@kbn/security-solution-common'; import { useInvestigationGuide } from '../../shared/hooks/use_investigation_guide'; import { useDocumentDetailsContext } from '../../shared/context'; import { INVESTIGATION_GUIDE_TEST_ID, INVESTIGATION_GUIDE_LOADING_TEST_ID } from './test_ids'; import { InvestigationGuideView } from './investigation_guide_view'; +import { FlyoutLoading } from '../../../shared/components/flyout_loading'; /** * Investigation guide displayed in the left panel. diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_alerts_by_ancestry.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_alerts_by_ancestry.test.tsx index 62012b72052bc..52468f0aedbb9 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_alerts_by_ancestry.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_alerts_by_ancestry.test.tsx @@ -20,7 +20,7 @@ import { EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID, EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID, EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID, -} from '@kbn/security-solution-common'; +} from '../../../shared/components/test_ids'; import { usePaginatedAlerts } from '../hooks/use_paginated_alerts'; jest.mock('../../shared/hooks/use_fetch_related_alerts_by_ancestry'); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_alerts_by_same_source_event.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_alerts_by_same_source_event.test.tsx index f4244fcc59a04..3cf2d93896bc3 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_alerts_by_same_source_event.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_alerts_by_same_source_event.test.tsx @@ -20,7 +20,7 @@ import { EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID, EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID, EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID, -} from '@kbn/security-solution-common'; +} from '../../../shared/components/test_ids'; import { usePaginatedAlerts } from '../hooks/use_paginated_alerts'; jest.mock('../../shared/hooks/use_fetch_related_alerts_by_same_source_event'); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_alerts_by_session.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_alerts_by_session.test.tsx index 54df7e8924f68..0120f462b9ac5 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_alerts_by_session.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_alerts_by_session.test.tsx @@ -21,7 +21,7 @@ import { EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID, EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID, EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID, -} from '@kbn/security-solution-common'; +} from '../../../shared/components/test_ids'; jest.mock('../../shared/hooks/use_fetch_related_alerts_by_session'); jest.mock('../hooks/use_paginated_alerts'); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_cases.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_cases.test.tsx index 4d90cc7f56133..db9eb7bdfb3ae 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_cases.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_cases.test.tsx @@ -18,7 +18,7 @@ import { EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID, EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID, EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID, -} from '@kbn/security-solution-common'; +} from '../../../shared/components/test_ids'; jest.mock('../../shared/hooks/use_fetch_related_cases'); jest.mock('../../../../common/components/links', () => ({ diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_cases.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_cases.tsx index 8e00e0e65478b..13df33a2deb1b 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_cases.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_cases.tsx @@ -10,7 +10,6 @@ import type { EuiBasicTableColumn } from '@elastic/eui'; import { EuiInMemoryTable } from '@elastic/eui'; import type { RelatedCase } from '@kbn/cases-plugin/common'; import { FormattedMessage } from '@kbn/i18n-react'; -import { ExpandablePanel } from '@kbn/security-solution-common'; import { CellTooltipWrapper } from '../../shared/components/cell_tooltip_wrapper'; import { CaseDetailsLink } from '../../../../common/components/links'; import { @@ -18,6 +17,7 @@ import { CORRELATIONS_DETAILS_CASES_SECTION_TEST_ID, } from './test_ids'; import { useFetchRelatedCases } from '../../shared/hooks/use_fetch_related_cases'; +import { ExpandablePanel } from '../../../shared/components/expandable_panel'; const ICON = 'warning'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/suppressed_alerts.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/suppressed_alerts.test.tsx index a97a601f082dd..3b73199494187 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/suppressed_alerts.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/suppressed_alerts.test.tsx @@ -17,7 +17,7 @@ import { EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID, EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID, EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID, -} from '@kbn/security-solution-common'; +} from '../../../shared/components/test_ids'; import { DocumentDetailsContext } from '../../shared/context'; import { mockContextValue } from '../../shared/mocks/mock_context'; import { isSuppressionRuleInGA } from '../../../../../common/detection_engine/utils'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/suppressed_alerts.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/suppressed_alerts.tsx index 2c24b5c595c52..c12c62733f4db 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/suppressed_alerts.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/suppressed_alerts.tsx @@ -12,8 +12,8 @@ import { ALERT_RULE_TYPE } from '@kbn/rule-data-utils'; import { EuiBetaBadge, EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import { ExpandablePanel } from '@kbn/security-solution-common'; import type { Type } from '@kbn/securitysolution-io-ts-alerting-types'; +import { ExpandablePanel } from '../../../shared/components/expandable_panel'; import { CORRELATIONS_DETAILS_SUPPRESSED_ALERTS_SECTION_TEST_ID, SUPPRESSED_ALERTS_SECTION_TECHNICAL_PREVIEW_TEST_ID, diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/threat_intelligence_details.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/threat_intelligence_details.tsx index 6ac43fbe5cd31..f473ae2c3262b 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/threat_intelligence_details.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/threat_intelligence_details.tsx @@ -9,7 +9,6 @@ import React, { memo } from 'react'; import { EuiHorizontalRule, EuiSpacer } from '@elastic/eui'; import isEmpty from 'lodash/isEmpty'; import { groupBy } from 'lodash'; -import { FlyoutLoading } from '@kbn/security-solution-common'; import { EnrichmentSection } from './threat_details_view_enrichment_section'; import { ENRICHMENT_TYPES } from '../../../../../common/cti/constants'; import { EnrichmentRangePicker } from './threat_intelligence_view_enrichment_range_picker'; @@ -20,6 +19,7 @@ import { THREAT_INTELLIGENCE_ENRICHMENTS_TEST_ID, THREAT_INTELLIGENCE_MATCHES_TEST_ID, } from './test_ids'; +import { FlyoutLoading } from '../../../shared/components/flyout_loading'; export const THREAT_INTELLIGENCE_TAB_ID = 'threatIntelligence'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/tour.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/tour.test.tsx deleted file mode 100644 index bb288b5c7afaa..0000000000000 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/tour.test.tsx +++ /dev/null @@ -1,122 +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, waitFor, fireEvent } from '@testing-library/react'; -import { LeftPanelTour } from './tour'; -import { DocumentDetailsContext } from '../../shared/context'; -import { mockContextValue } from '../../shared/mocks/mock_context'; -import { - createMockStore, - createSecuritySolutionStorageMock, - TestProviders, -} from '../../../../common/mock'; -import { useKibana as mockUseKibana } from '../../../../common/lib/kibana/__mocks__'; -import { useKibana } from '../../../../common/lib/kibana'; -import { FLYOUT_TOUR_CONFIG_ANCHORS } from '../../shared/utils/tour_step_config'; -import { FLYOUT_TOUR_TEST_ID } from '../../shared/components/test_ids'; -import { useWhichFlyout } from '../../shared/hooks/use_which_flyout'; -import { Flyouts } from '../../shared/constants/flyouts'; - -jest.mock('../../../../common/lib/kibana'); -jest.mock('../../shared/hooks/use_which_flyout'); - -const mockedUseKibana = mockUseKibana(); - -const { storage: storageMock } = createSecuritySolutionStorageMock(); -const mockStore = createMockStore(undefined, undefined, undefined, { - ...storageMock, -}); - -const renderLeftPanelTour = (context: DocumentDetailsContext = mockContextValue) => - render( - - - - {Object.values(FLYOUT_TOUR_CONFIG_ANCHORS).map((i, idx) => ( -
- ))} - - - ); - -describe('', () => { - beforeEach(() => { - (useKibana as jest.Mock).mockReturnValue({ - ...mockedUseKibana, - services: { - ...mockedUseKibana.services, - storage: storageMock, - }, - }); - (useWhichFlyout as jest.Mock).mockReturnValue(Flyouts.securitySolution); - - storageMock.clear(); - }); - - it('should render left panel tour for alerts starting as step 4', async () => { - storageMock.set('securitySolution.documentDetails.newFeaturesTour.v8.14', { - currentTourStep: 4, - isTourActive: true, - }); - - const { getByText, getByTestId } = renderLeftPanelTour(); - await waitFor(() => { - expect(getByTestId(`${FLYOUT_TOUR_TEST_ID}-4`)).toBeVisible(); - }); - fireEvent.click(getByText('Next')); - await waitFor(() => { - expect(getByTestId(`${FLYOUT_TOUR_TEST_ID}-5`)).toBeVisible(); - }); - await waitFor(() => { - expect(getByText('Finish')).toBeVisible(); - }); - }); - - it('should not render left panel tour for preview', () => { - storageMock.set('securitySolution.documentDetails.newFeaturesTour.v8.14', { - currentTourStep: 3, - isTourActive: true, - }); - - const { queryByTestId, queryByText } = renderLeftPanelTour({ - ...mockContextValue, - isPreview: true, - }); - expect(queryByTestId(`${FLYOUT_TOUR_TEST_ID}-4`)).not.toBeInTheDocument(); - expect(queryByText('Next')).not.toBeInTheDocument(); - }); - - it('should not render left panel tour for non-alerts', async () => { - storageMock.set('securitySolution.documentDetails.newFeaturesTour.v8.14', { - currentTourStep: 3, - isTourActive: true, - }); - - const { queryByTestId, queryByText } = renderLeftPanelTour({ - ...mockContextValue, - getFieldsData: () => '', - }); - expect(queryByTestId(`${FLYOUT_TOUR_TEST_ID}-4`)).not.toBeInTheDocument(); - expect(queryByText('Next')).not.toBeInTheDocument(); - }); - - it('should not render left panel tour for flyout in timeline', () => { - (useWhichFlyout as jest.Mock).mockReturnValue(Flyouts.timeline); - storageMock.set('securitySolution.documentDetails.newFeaturesTour.v8.14', { - currentTourStep: 3, - isTourActive: true, - }); - - const { queryByTestId, queryByText } = renderLeftPanelTour({ - ...mockContextValue, - isPreview: true, - }); - expect(queryByTestId(`${FLYOUT_TOUR_TEST_ID}-4`)).not.toBeInTheDocument(); - expect(queryByText('Next')).not.toBeInTheDocument(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/tour.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/tour.tsx deleted file mode 100644 index 196d9113457a3..0000000000000 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/tour.tsx +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { memo, useMemo } from 'react'; -import { useWhichFlyout } from '../../shared/hooks/use_which_flyout'; -import { getField } from '../../shared/utils'; -import { EventKind } from '../../shared/constants/event_kinds'; -import { useDocumentDetailsContext } from '../../shared/context'; -import { getLeftSectionTourSteps } from '../../shared/utils/tour_step_config'; -import { Flyouts } from '../../shared/constants/flyouts'; -import { FlyoutTour } from '../../shared/components/flyout_tour'; - -/** - * Guided tour for the left panel in details flyout - */ -export const LeftPanelTour = memo(() => { - const { getFieldsData, isPreview } = useDocumentDetailsContext(); - const eventKind = getField(getFieldsData('event.kind')); - const isAlert = eventKind === EventKind.signal; - const isTimelineFlyoutOpen = useWhichFlyout() === Flyouts.timeline; - const showTour = isAlert && !isPreview && !isTimelineFlyoutOpen; - - const tourStepContent = useMemo(() => getLeftSectionTourSteps(), []); - - return showTour ? : null; -}); - -LeftPanelTour.displayName = 'LeftPanelTour'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.test.tsx index d4150c01d06a6..966253b3d27ed 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.test.tsx @@ -28,7 +28,7 @@ import { USER_DETAILS_MISCONFIGURATIONS_TEST_ID, USER_DETAILS_ALERT_COUNT_TEST_ID, } from './test_ids'; -import { EXPANDABLE_PANEL_CONTENT_TEST_ID } from '@kbn/security-solution-common'; +import { EXPANDABLE_PANEL_CONTENT_TEST_ID } from '../../../shared/components/test_ids'; import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score'; import { mockContextValue } from '../../shared/mocks/mock_context'; import { mockFlyoutApi } from '../../shared/mocks/mock_flyout_context'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.tsx index c90d11f4b8bc2..ed406d3b8c679 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.tsx @@ -25,8 +25,8 @@ import type { EuiBasicTableColumn } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import { ExpandablePanel } from '@kbn/security-solution-common'; import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; +import { ExpandablePanel } from '../../../shared/components/expandable_panel'; import type { RelatedHost } from '../../../../../common/search_strategy/security_solution/related_entities/related_hosts'; import type { RiskSeverity } from '../../../../../common/search_strategy'; import { UserOverview } from '../../../../overview/components/user_overview'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/content.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/content.tsx index 226765e6c4e37..53d2efe883397 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/content.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/content.tsx @@ -9,9 +9,9 @@ import { useEuiBackgroundColor } from '@elastic/eui'; import type { FC } from 'react'; import React, { useMemo } from 'react'; import { css } from '@emotion/react'; -import { FlyoutBody } from '@kbn/security-solution-common'; import type { LeftPanelPaths } from '.'; import type { LeftPanelTabType } from './tabs'; +import { FlyoutBody } from '../../shared/components/flyout_body'; export interface PanelContentProps { /** diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/header.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/header.tsx index f276ccca842be..2b61a97577e06 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/header.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/header.tsx @@ -9,8 +9,8 @@ import { EuiTab, EuiTabs, useEuiBackgroundColor } from '@elastic/eui'; import type { FC } from 'react'; import React, { memo } from 'react'; import { css } from '@emotion/react'; -import { FlyoutHeader } from '@kbn/security-solution-common'; import type { LeftPanelPaths } from '.'; +import { FlyoutHeader } from '../../shared/components/flyout_header'; import type { LeftPanelTabType } from './tabs'; import { getField } from '../shared/utils'; import { EventKind } from '../shared/constants/event_kinds'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/index.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/index.tsx index 8e6b817e275e5..56375426c5f68 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/index.tsx @@ -22,7 +22,6 @@ import { getField } from '../shared/utils'; import { EventKind } from '../shared/constants/event_kinds'; import { useDocumentDetailsContext } from '../shared/context'; import type { DocumentDetailsProps } from '../shared/types'; -import { LeftPanelTour } from './components/tour'; export type LeftPanelPaths = 'visualize' | 'insights' | 'investigation' | 'response' | 'notes'; export const LeftPanelVisualizeTab: LeftPanelPaths = 'visualize'; @@ -36,8 +35,8 @@ export const LeftPanel: FC> = memo(({ path }) => { const { openLeftPanel } = useExpandableFlyoutApi(); const { eventId, indexName, scopeId, getFieldsData, isPreview } = useDocumentDetailsContext(); const eventKind = getField(getFieldsData('event.kind')); - const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled( - 'securitySolutionNotesEnabled' + const securitySolutionNotesDisabled = useIsExperimentalFeatureEnabled( + 'securitySolutionNotesDisabled' ); const [visualizationInFlyoutEnabled] = useUiSetting$( @@ -49,14 +48,14 @@ export const LeftPanel: FC> = memo(({ path }) => { eventKind === EventKind.signal ? [tabs.insightsTab, tabs.investigationTab, tabs.responseTab] : [tabs.insightsTab]; - if (securitySolutionNotesEnabled && !isPreview) { + if (!securitySolutionNotesDisabled && !isPreview) { tabList.push(tabs.notesTab); } if (visualizationInFlyoutEnabled && !isPreview) { return [tabs.visualizeTab, ...tabList]; } return tabList; - }, [eventKind, isPreview, securitySolutionNotesEnabled, visualizationInFlyoutEnabled]); + }, [eventKind, isPreview, securitySolutionNotesDisabled, visualizationInFlyoutEnabled]); const selectedTabId = useMemo(() => { const defaultTab = tabsDisplayed[0].id; @@ -85,7 +84,6 @@ export const LeftPanel: FC> = memo(({ path }) => { return ( <> - - -
+ ), 'data-test-subj': INSIGHTS_TAB_ENTITIES_BUTTON_TEST_ID, }, @@ -62,12 +58,10 @@ const insightsButtons: EuiButtonGroupOptionProps[] = [ { id: PREVALENCE_TAB_ID, label: ( -
- -
+ ), 'data-test-subj': INSIGHTS_TAB_PREVALENCE_BUTTON_TEST_ID, }, diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/tabs/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/document_details/left/tabs/test_ids.ts index 1e99fb63d18a5..eb64c91b2143d 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/tabs/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/tabs/test_ids.ts @@ -17,12 +17,8 @@ const INSIGHTS_TAB_TEST_ID = `${PREFIX}InsightsTab` as const; export const INSIGHTS_TAB_BUTTON_GROUP_TEST_ID = `${INSIGHTS_TAB_TEST_ID}ButtonGroup` as const; export const INSIGHTS_TAB_ENTITIES_BUTTON_TEST_ID = `${INSIGHTS_TAB_TEST_ID}EntitiesButton` as const; -export const INSIGHTS_TAB_ENTITIES_BUTTON_LABEL_TEST_ID = - `${INSIGHTS_TAB_TEST_ID}Entities` as const; export const INSIGHTS_TAB_THREAT_INTELLIGENCE_BUTTON_TEST_ID = `${INSIGHTS_TAB_TEST_ID}ThreatIntelligenceButton` as const; -export const INSIGHTS_TAB_PREVALENCE_BUTTON_LABEL_TEST_ID = - `${INSIGHTS_TAB_TEST_ID}Prevalence` as const; export const INSIGHTS_TAB_PREVALENCE_BUTTON_TEST_ID = `${INSIGHTS_TAB_TEST_ID}PrevalenceButton` as const; export const INSIGHTS_TAB_CORRELATIONS_BUTTON_TEST_ID = diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/document_details/left/test_ids.ts index 4e2c1be90b01c..9f5eeb035786c 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/test_ids.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { PREFIX } from '@kbn/security-solution-common'; +import { PREFIX } from '../../shared/test_ids'; export const VISUALIZE_TAB_TEST_ID = `${PREFIX}VisualizeTab` as const; export const INSIGHTS_TAB_TEST_ID = `${PREFIX}InsightsTab` as const; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/preview/footer.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/preview/footer.tsx index f437c9e77a158..0201332888675 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/preview/footer.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/preview/footer.tsx @@ -9,9 +9,9 @@ import { EuiLink, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import { FlyoutFooter } from '@kbn/security-solution-common'; import { getField } from '../shared/utils'; import { EventKind } from '../shared/constants/event_kinds'; +import { FlyoutFooter } from '../../shared/components/flyout_footer'; import { DocumentDetailsRightPanelKey } from '../shared/constants/panel_keys'; import { useDocumentDetailsContext } from '../shared/context'; import { PREVIEW_FOOTER_TEST_ID, PREVIEW_FOOTER_LINK_TEST_ID } from './test_ids'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.test.tsx index 6d72ca9e58dfa..8a8293badb6af 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.test.tsx @@ -78,7 +78,7 @@ describe('', () => { }); it('should render notes section if experimental flag is enabled', () => { - (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true); + (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false); const { getByTestId } = renderHeader(mockContextValue); expect(getByTestId(NOTES_TITLE_TEST_ID)).toBeInTheDocument(); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.tsx index f128fada5fb29..cc7ef14585833 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.tsx @@ -9,7 +9,6 @@ import React, { memo, useCallback, useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiLink } from '@elastic/eui'; import { ALERT_WORKFLOW_ASSIGNEE_IDS } from '@kbn/rule-data-utils'; import { i18n } from '@kbn/i18n'; -import { FlyoutTitle } from '@kbn/security-solution-common'; import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { Notes } from './notes'; import { useRuleDetailsLink } from '../../shared/hooks/use_rule_details_link'; @@ -22,6 +21,7 @@ import { useDocumentDetailsContext } from '../../shared/context'; import { PreferenceFormattedDate } from '../../../../common/components/formatted_date'; import { FLYOUT_ALERT_HEADER_TITLE_TEST_ID, ALERT_SUMMARY_PANEL_TEST_ID } from './test_ids'; import { Assignees } from './assignees'; +import { FlyoutTitle } from '../../../shared/components/flyout_title'; // minWidth for each block, allows to switch for a 1 row 4 blocks to 2 rows with 2 block each const blockStyles = { @@ -40,8 +40,8 @@ export const AlertHeaderTitle = memo(() => { refetchFlyoutData, getFieldsData, } = useDocumentDetailsContext(); - const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled( - 'securitySolutionNotesEnabled' + const securitySolutionNotesDisabled = useIsExperimentalFeatureEnabled( + 'securitySolutionNotesDisabled' ); const { isAlert, ruleName, timestamp, ruleId } = useBasicDataFromDetailsData( @@ -98,7 +98,30 @@ export const AlertHeaderTitle = memo(() => { /> )} - {securitySolutionNotesEnabled ? ( + {securitySolutionNotesDisabled ? ( + + + + + + + + + + + + ) : ( {
- ) : ( - - - - - - - - - - - )} ); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.test.tsx index f9179cecc6b5a..3e841da34a4fa 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.test.tsx @@ -21,7 +21,8 @@ import { EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID, EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID, EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID, -} from '@kbn/security-solution-common'; +} from '../../../shared/components/test_ids'; + import { useInvestigateInTimeline } from '../../../../detections/components/alerts_table/timeline_actions/use_investigate_in_timeline'; jest.mock( 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 6896f15ca88cb..286394d16aadc 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 @@ -10,7 +10,6 @@ import { useDispatch } from 'react-redux'; import { TimelineTabs } from '@kbn/securitysolution-data-table'; import { EuiLink, EuiMark } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { ExpandablePanel } from '@kbn/security-solution-common'; import { useUiSetting$ } from '@kbn/kibana-react-plugin/public'; import { ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING } from '../../../../../common/constants'; import { useStartTransaction } from '../../../../common/lib/apm/use_start_transaction'; @@ -23,6 +22,7 @@ import { useIsInvestigateInResolverActionEnabled } from '../../../../detections/ import { AnalyzerPreview } from './analyzer_preview'; import { ANALYZER_PREVIEW_TEST_ID } from './test_ids'; import { useNavigateToAnalyzer } from '../../shared/hooks/use_navigate_to_analyzer'; +import { ExpandablePanel } from '../../../shared/components/expandable_panel'; const timelineId = 'timeline-1'; 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 f4a97b7deed11..c9d8606cf9343 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 @@ -22,7 +22,8 @@ import { CORRELATIONS_RELATED_CASES_TEST_ID, CORRELATIONS_SUPPRESSED_ALERTS_TEST_ID, CORRELATIONS_TEST_ID, - SUMMARY_ROW_VALUE_TEST_ID, + SUMMARY_ROW_BUTTON_TEST_ID, + SUMMARY_ROW_TEXT_TEST_ID, } from './test_ids'; import { useShowRelatedAlertsByAncestry } from '../../shared/hooks/use_show_related_alerts_by_ancestry'; import { useShowRelatedAlertsBySameSourceEvent } from '../../shared/hooks/use_show_related_alerts_by_same_source_event'; @@ -39,7 +40,7 @@ import { EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID, EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID, EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID, -} from '@kbn/security-solution-common'; +} from '../../../shared/components/test_ids'; import { useTourContext } from '../../../../common/components/guided_onboarding_tour'; import { AlertsCasesTourSteps } from '../../../../common/components/guided_onboarding_tour/tour_config'; @@ -58,17 +59,32 @@ const TITLE_LINK_TEST_ID = EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(CORRELATIO const TITLE_ICON_TEST_ID = EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID(CORRELATIONS_TEST_ID); const TITLE_TEXT_TEST_ID = EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(CORRELATIONS_TEST_ID); -const SUPPRESSED_ALERTS_TEST_ID = SUMMARY_ROW_VALUE_TEST_ID(CORRELATIONS_SUPPRESSED_ALERTS_TEST_ID); -const RELATED_ALERTS_BY_ANCESTRY_TEST_ID = SUMMARY_ROW_VALUE_TEST_ID( +const SUPPRESSED_ALERTS_TEXT_TEST_ID = SUMMARY_ROW_TEXT_TEST_ID( + CORRELATIONS_SUPPRESSED_ALERTS_TEST_ID +); +const SUPPRESSED_ALERTS_VALUE_TEST_ID = SUMMARY_ROW_BUTTON_TEST_ID( + CORRELATIONS_SUPPRESSED_ALERTS_TEST_ID +); +const RELATED_ALERTS_BY_ANCESTRY_TEXT_TEST_ID = SUMMARY_ROW_TEXT_TEST_ID( + CORRELATIONS_RELATED_ALERTS_BY_ANCESTRY_TEST_ID +); +const RELATED_ALERTS_BY_ANCESTRY_VALUE_TEST_ID = SUMMARY_ROW_BUTTON_TEST_ID( CORRELATIONS_RELATED_ALERTS_BY_ANCESTRY_TEST_ID ); -const RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEST_ID = SUMMARY_ROW_VALUE_TEST_ID( +const RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEXT_TEST_ID = SUMMARY_ROW_TEXT_TEST_ID( CORRELATIONS_RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEST_ID ); -const RELATED_ALERTS_BY_SESSION_TEST_ID = SUMMARY_ROW_VALUE_TEST_ID( +const RELATED_ALERTS_BY_SAME_SOURCE_EVENT_VALUE_TEST_ID = SUMMARY_ROW_BUTTON_TEST_ID( + CORRELATIONS_RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEST_ID +); +const RELATED_ALERTS_BY_SESSION_TEXT_TEST_ID = SUMMARY_ROW_TEXT_TEST_ID( + CORRELATIONS_RELATED_ALERTS_BY_SESSION_TEST_ID +); +const RELATED_ALERTS_BY_SESSION_VALUE_TEST_ID = SUMMARY_ROW_BUTTON_TEST_ID( CORRELATIONS_RELATED_ALERTS_BY_SESSION_TEST_ID ); -const RELATED_CASES_TEST_ID = SUMMARY_ROW_VALUE_TEST_ID(CORRELATIONS_RELATED_CASES_TEST_ID); +const RELATED_CASES_TEXT_TEST_ID = SUMMARY_ROW_TEXT_TEST_ID(CORRELATIONS_RELATED_CASES_TEST_ID); +const RELATED_CASES_VALUE_TEST_ID = SUMMARY_ROW_BUTTON_TEST_ID(CORRELATIONS_RELATED_CASES_TEST_ID); const panelContextValue = { eventId: 'event id', @@ -193,11 +209,16 @@ describe('', () => { }); const { getByTestId, queryByText } = render(renderCorrelationsOverview(panelContextValue)); - expect(getByTestId(RELATED_ALERTS_BY_ANCESTRY_TEST_ID)).toBeInTheDocument(); - expect(getByTestId(RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEST_ID)).toBeInTheDocument(); - expect(getByTestId(RELATED_ALERTS_BY_SESSION_TEST_ID)).toBeInTheDocument(); - expect(getByTestId(RELATED_CASES_TEST_ID)).toBeInTheDocument(); - expect(getByTestId(SUPPRESSED_ALERTS_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(RELATED_ALERTS_BY_ANCESTRY_TEXT_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(RELATED_ALERTS_BY_ANCESTRY_VALUE_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEXT_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(RELATED_ALERTS_BY_SAME_SOURCE_EVENT_VALUE_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(RELATED_ALERTS_BY_SESSION_TEXT_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(RELATED_ALERTS_BY_SESSION_VALUE_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(RELATED_CASES_TEXT_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(RELATED_CASES_VALUE_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(SUPPRESSED_ALERTS_TEXT_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(SUPPRESSED_ALERTS_VALUE_TEST_ID)).toBeInTheDocument(); expect(queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument(); }); @@ -215,11 +236,18 @@ describe('', () => { jest.mocked(useShowSuppressedAlerts).mockReturnValue({ show: false, alertSuppressionCount: 0 }); const { getByText, queryByTestId } = render(renderCorrelationsOverview(panelContextValue)); - expect(queryByTestId(RELATED_ALERTS_BY_ANCESTRY_TEST_ID)).not.toBeInTheDocument(); - expect(queryByTestId(RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEST_ID)).not.toBeInTheDocument(); - expect(queryByTestId(RELATED_ALERTS_BY_SESSION_TEST_ID)).not.toBeInTheDocument(); - expect(queryByTestId(RELATED_CASES_TEST_ID)).not.toBeInTheDocument(); - expect(queryByTestId(SUPPRESSED_ALERTS_TEST_ID)).not.toBeInTheDocument(); + expect(queryByTestId(RELATED_ALERTS_BY_ANCESTRY_TEXT_TEST_ID)).not.toBeInTheDocument(); + expect(queryByTestId(RELATED_ALERTS_BY_ANCESTRY_VALUE_TEST_ID)).not.toBeInTheDocument(); + expect(queryByTestId(RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEXT_TEST_ID)).not.toBeInTheDocument(); + expect( + queryByTestId(RELATED_ALERTS_BY_SAME_SOURCE_EVENT_VALUE_TEST_ID) + ).not.toBeInTheDocument(); + expect(queryByTestId(RELATED_ALERTS_BY_SESSION_TEXT_TEST_ID)).not.toBeInTheDocument(); + expect(queryByTestId(RELATED_ALERTS_BY_SESSION_VALUE_TEST_ID)).not.toBeInTheDocument(); + expect(queryByTestId(RELATED_CASES_TEXT_TEST_ID)).not.toBeInTheDocument(); + expect(queryByTestId(RELATED_CASES_VALUE_TEST_ID)).not.toBeInTheDocument(); + expect(queryByTestId(SUPPRESSED_ALERTS_TEXT_TEST_ID)).not.toBeInTheDocument(); + expect(queryByTestId(SUPPRESSED_ALERTS_VALUE_TEST_ID)).not.toBeInTheDocument(); expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument(); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.tsx index c2494b4cde675..9ba55f0d041f6 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.tsx @@ -12,8 +12,8 @@ import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; import { FormattedMessage } from '@kbn/i18n-react'; import { ALERT_RULE_TYPE } from '@kbn/rule-data-utils'; -import { ExpandablePanel } from '@kbn/security-solution-common'; import type { Type } from '@kbn/securitysolution-io-ts-alerting-types'; +import { ExpandablePanel } from '../../../shared/components/expandable_panel'; import { useShowRelatedAlertsBySession } from '../../shared/hooks/use_show_related_alerts_by_session'; import { RelatedAlertsBySession } from './related_alerts_by_session'; import { useShowRelatedAlertsBySameSourceEvent } from '../../shared/hooks/use_show_related_alerts_by_same_source_event'; @@ -134,7 +134,7 @@ export const CorrelationsOverview: React.FC = () => { data-test-subj={CORRELATIONS_TEST_ID} > {canShowAtLeastOneInsight ? ( - + {showSuppressedAlerts && ( )} diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/entities_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/entities_overview.test.tsx index e4975d3136424..92248c6de2828 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/entities_overview.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/entities_overview.test.tsx @@ -24,7 +24,7 @@ import { EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID, EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID, EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID, -} from '@kbn/security-solution-common'; +} from '../../../shared/components/test_ids'; import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score'; const from = '2022-04-05T12:00:00.000Z'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/entities_overview.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/entities_overview.tsx index 60657a1346101..16fe6cbe1c1e0 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/entities_overview.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/entities_overview.tsx @@ -9,8 +9,8 @@ import React, { useCallback, useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; import { FormattedMessage } from '@kbn/i18n-react'; -import { ExpandablePanel } from '@kbn/security-solution-common'; import { INSIGHTS_ENTITIES_TEST_ID } from './test_ids'; +import { ExpandablePanel } from '../../../shared/components/expandable_panel'; import { useDocumentDetailsContext } from '../../shared/context'; import { getField } from '../../shared/utils'; import { HostEntityOverview } from './host_entity_overview'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/event_header_title.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/event_header_title.tsx index f93b8451c744a..953a2371ffa88 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/event_header_title.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/event_header_title.tsx @@ -9,7 +9,7 @@ import React, { memo, useMemo } from 'react'; import { startCase } from 'lodash'; import { EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FlyoutTitle } from '@kbn/security-solution-common'; +import { FlyoutTitle } from '../../../shared/components/flyout_title'; import { DocumentSeverity } from './severity'; import { useBasicDataFromDetailsData } from '../../shared/hooks/use_basic_data_from_details_data'; import { useDocumentDetailsContext } from '../../shared/context'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.test.tsx index 6b30e2127a2f8..d44321a4926bd 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.test.tsx @@ -14,14 +14,13 @@ import { GraphPreviewContainer } from './graph_preview_container'; import { GRAPH_PREVIEW_TEST_ID } from './test_ids'; import { useGraphPreview } from '../hooks/use_graph_preview'; import { useFetchGraphData } from '../hooks/use_fetch_graph_data'; - import { EXPANDABLE_PANEL_CONTENT_TEST_ID, EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID, EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID, EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID, EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID, -} from '@kbn/security-solution-common'; +} from '../../../shared/components/test_ids'; jest.mock('../hooks/use_graph_preview'); jest.mock('../hooks/use_fetch_graph_data', () => ({ diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.tsx index 1bc6a8dd7e547..be65593364593 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.tsx @@ -7,12 +7,12 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { ExpandablePanel } from '@kbn/security-solution-common'; import { useDocumentDetailsContext } from '../../shared/context'; import { GRAPH_PREVIEW_TEST_ID } from './test_ids'; import { GraphPreview } from './graph_preview'; import { useFetchGraphData } from '../hooks/use_fetch_graph_data'; import { useGraphPreview } from '../hooks/use_graph_preview'; +import { ExpandablePanel } from '../../../shared/components/expandable_panel'; const DEFAULT_FROM = 'now-60d/d'; const DEFAULT_TO = 'now/d'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_summary_row.stories.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_summary_row.stories.tsx deleted file mode 100644 index eb76108d6b215..0000000000000 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_summary_row.stories.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 React from 'react'; -import type { Story } from '@storybook/react'; -import { css } from '@emotion/react'; -import { InsightsSummaryRow } from './insights_summary_row'; - -export default { - component: InsightsSummaryRow, - title: 'Flyout/InsightsSummaryRow', -}; - -const wrapper = (children: React.ReactNode) => ( -
- {children} -
-); - -export const Default: Story = () => - wrapper( - - ); - -export const InvalidColor: Story = () => - wrapper( - - ); - -export const NoColor: Story = () => - wrapper(); - -export const LongText: Story = () => - wrapper( - - ); - -export const LongNumber: Story = () => - wrapper( - - ); - -export const Loading: Story = () => - wrapper(); - -export const Error: Story = () => - wrapper(); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_summary_row.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_summary_row.test.tsx index 3e10e83332a97..2a721e317781e 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_summary_row.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_summary_row.test.tsx @@ -9,74 +9,147 @@ import React from 'react'; import { render } from '@testing-library/react'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { InsightsSummaryRow } from './insights_summary_row'; +import { useDocumentDetailsContext } from '../../shared/context'; +import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; +import { DocumentDetailsLeftPanelKey } from '../../shared/constants/panel_keys'; +import { LeftPanelInsightsTab } from '../../left'; + +jest.mock('@kbn/expandable-flyout'); +jest.mock('../../shared/context'); + +const mockOpenLeftPanel = jest.fn(); +const scopeId = 'scopeId'; +const eventId = 'eventId'; +const indexName = 'indexName'; const testId = 'test'; -const iconTestId = `${testId}Icon`; +const textTestId = `${testId}Text`; +const buttonTestId = `${testId}Button`; const valueTestId = `${testId}Value`; -const colorTestId = `${testId}Color`; const loadingTestId = `${testId}Loading`; describe('', () => { - it('should render by default', () => { + beforeEach(() => { + jest.clearAllMocks(); + + (useDocumentDetailsContext as jest.Mock).mockReturnValue({ + eventId, + indexName, + scopeId, + isPreviewMode: false, + }); + (useExpandableFlyoutApi as jest.Mock).mockReturnValue({ openLeftPanel: mockOpenLeftPanel }); + }); + + it('should render loading skeleton if loading is true', () => { const { getByTestId } = render( + {'value for this'}
} + data-test-subj={testId} + /> + ); + + expect(getByTestId(loadingTestId)).toBeInTheDocument(); + }); + + it('should only render null when error is true', () => { + const { container } = render( + {'value for this'}
} /> + ); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should render the value component', () => { + const { getByTestId, queryByTestId } = render( {'value for this'}
} data-test-subj={testId} /> ); - expect(getByTestId(iconTestId)).toBeInTheDocument(); - expect(getByTestId(valueTestId)).toHaveTextContent('1 this is a test for red'); - expect(getByTestId(colorTestId)).toBeInTheDocument(); + expect(getByTestId(textTestId)).toHaveTextContent('this is a test for red'); + expect(getByTestId(valueTestId)).toHaveTextContent('value for this'); + expect(queryByTestId(buttonTestId)).not.toBeInTheDocument(); }); - it('should render loading skeletton if loading is true', () => { - const { getByTestId } = render( - + it('should render the value as EuiBadge and EuiButtonEmpty', () => { + const { getByTestId, queryByTestId } = render( + + + ); - expect(getByTestId(loadingTestId)).toBeInTheDocument(); + expect(getByTestId(textTestId)).toHaveTextContent('this is a test for red'); + expect(getByTestId(buttonTestId)).toHaveTextContent('2'); + expect(queryByTestId(valueTestId)).not.toBeInTheDocument(); }); - it('should only render null when error is true', () => { - const { container } = render(); + it('should render big numbers formatted correctly', () => { + const { getByTestId } = render( + + + + ); - expect(container).toBeEmptyDOMElement(); + expect(getByTestId(buttonTestId)).toHaveTextContent('2k'); }); - it('should handle big number in a compact notation', () => { + it('should open the expanded section to the correct tab when the number is clicked', () => { const { getByTestId } = render( ); + getByTestId(buttonTestId).click(); - expect(getByTestId(valueTestId)).toHaveTextContent('160k this is a test for red'); + expect(mockOpenLeftPanel).toHaveBeenCalledWith({ + id: DocumentDetailsLeftPanelKey, + path: { + tab: LeftPanelInsightsTab, + subTab: 'subTab', + }, + params: { + id: eventId, + indexName, + scopeId, + }, + }); }); - it(`should not show the colored dot if color isn't provided`, () => { - const { queryByTestId } = render( + it('should disabled the click when in preview mode', () => { + (useDocumentDetailsContext as jest.Mock).mockReturnValue({ + eventId, + indexName, + scopeId, + isPreviewMode: true, + }); + + const { getByTestId } = render( ); + const button = getByTestId(buttonTestId); + + expect(button).toHaveAttribute('disabled'); - expect(queryByTestId(colorTestId)).not.toBeInTheDocument(); + button.click(); + expect(mockOpenLeftPanel).not.toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_summary_row.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_summary_row.tsx index 23f838f5068bb..56a19d2eca965 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_summary_row.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_summary_row.tsx @@ -6,19 +6,25 @@ */ import type { ReactElement, VFC } from 'react'; -import React from 'react'; +import React, { useMemo, useCallback } from 'react'; import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; -import { - EuiIcon, - EuiFlexGroup, - EuiFlexItem, - EuiHealth, - EuiSkeletonText, - useEuiTheme, -} from '@elastic/eui'; +import { EuiBadge, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiSkeletonText } from '@elastic/eui'; +import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; +import { useDocumentDetailsContext } from '../../shared/context'; +import { DocumentDetailsLeftPanelKey } from '../../shared/constants/panel_keys'; +import { LeftPanelInsightsTab } from '../../left'; import { FormattedCount } from '../../../../common/components/formatted_number'; +const LOADING = i18n.translate( + 'xpack.securitySolution.flyout.right.insights.insightSummaryLoadingAriaLabel', + { defaultMessage: 'Loading' } +); +const BUTTON = i18n.translate( + 'xpack.securitySolution.flyout.right.insights.insightSummaryButtonAriaLabel', + { defaultMessage: 'Click to see more details' } +); + export interface InsightsSummaryRowProps { /** * Optional parameter used to display a loading spinner @@ -29,22 +35,17 @@ export interface InsightsSummaryRowProps { */ error?: boolean; /** - * Icon to display on the left side of each row + * Text corresponding of the number of results/entries */ - icon: string; + text: string | ReactElement; /** * Number of results/entries found */ - value?: number; + value: number | ReactElement; /** - * Text corresponding of the number of results/entries + * Optional parameter used to know which subtab to navigate to when the user clicks on the button */ - text: string | ReactElement; - /** - * Optional parameter for now, will be used to display a dot on the right side - * (corresponding to some sort of severity?) - */ - color?: string; // TODO remove optional when we have guidance on what the colors will actually be + expandedSubTab?: string; /** * Prefix data-test-subj because this component will be used in multiple places */ @@ -52,35 +53,73 @@ export interface InsightsSummaryRowProps { } /** - * Panel showing summary information as an icon, a count and text as well as a severity colored dot. + * Panel showing summary information. + * The default display is a text on the left and a count on the right, displayed with a clickable EuiBadge. + * The left and right section can accept a ReactElement to allow for more complex display. * Should be used for Entities, Threat intelligence, Prevalence, Correlations and Results components under the Insights section. - * The colored dot is currently optional but will ultimately be mandatory (waiting on PM and UIUX). */ export const InsightsSummaryRow: VFC = ({ loading = false, error = false, - icon, value, text, - color, + expandedSubTab, 'data-test-subj': dataTestSubj, }) => { - const { euiTheme } = useEuiTheme(); + const { eventId, indexName, scopeId, isPreviewMode } = useDocumentDetailsContext(); + const { openLeftPanel } = useExpandableFlyoutApi(); + + const onClick = useCallback(() => { + openLeftPanel({ + id: DocumentDetailsLeftPanelKey, + path: { + tab: LeftPanelInsightsTab, + subTab: expandedSubTab, + }, + params: { + id: eventId, + indexName, + scopeId, + }, + }); + }, [eventId, expandedSubTab, indexName, openLeftPanel, scopeId]); + + const textDataTestSubj = useMemo(() => `${dataTestSubj}Text`, [dataTestSubj]); + const loadingDataTestSubj = useMemo(() => `${dataTestSubj}Loading`, [dataTestSubj]); + + const button = useMemo(() => { + const buttonDataTestSubj = `${dataTestSubj}Button`; + const valueDataTestSubj = `${dataTestSubj}Value`; + + return ( + <> + {typeof value === 'number' ? ( + + + + + + ) : ( +
{value}
+ )} + + ); + }, [dataTestSubj, isPreviewMode, onClick, value]); - const loadingDataTestSubj = `${dataTestSubj}Loading`; if (loading) { return ( ); @@ -90,10 +129,6 @@ export const InsightsSummaryRow: VFC = ({ return null; } - const iconDataTestSubj = `${dataTestSubj}Icon`; - const valueDataTestSubj = `${dataTestSubj}Value`; - const colorDataTestSubj = `${dataTestSubj}Color`; - return ( = ({ alignItems={'center'} responsive={false} > - - - = ({ overflow: hidden; `} > - {value && } {text} + {text} - {color && ( - - - - )} + {button} ); }; 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 a47ed04c85b5a..57770b58e2fcb 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 @@ -8,7 +8,11 @@ import { render } from '@testing-library/react'; import { TestProviders } from '../../../../common/mock'; import { DocumentDetailsContext } from '../../shared/context'; -import { PREVALENCE_TEST_ID } from './test_ids'; +import { + PREVALENCE_TEST_ID, + SUMMARY_ROW_TEXT_TEST_ID, + SUMMARY_ROW_VALUE_TEST_ID, +} from './test_ids'; import { DocumentDetailsLeftPanelKey } from '../../shared/constants/panel_keys'; import { LeftPanelInsightsTab } from '../../left'; import React from 'react'; @@ -20,7 +24,7 @@ import { EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID, EXPANDABLE_PANEL_LOADING_TEST_ID, EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID, -} from '@kbn/security-solution-common'; +} from '../../../shared/components/test_ids'; import { usePrevalence } from '../../shared/hooks/use_prevalence'; import { mockContextValue } from '../../shared/mocks/mock_context'; import { type ExpandableFlyoutApi, useExpandableFlyoutApi } from '@kbn/expandable-flyout'; @@ -38,7 +42,10 @@ const flyoutContextValue = { openLeftPanel: jest.fn(), } as unknown as ExpandableFlyoutApi; -jest.mock('@kbn/expandable-flyout'); +jest.mock('@kbn/expandable-flyout', () => ({ + useExpandableFlyoutApi: jest.fn(), + ExpandableFlyoutProvider: ({ children }: React.PropsWithChildren<{}>) => <>{children}, +})); const renderPrevalenceOverview = (contextValue: DocumentDetailsContext = mockContextValue) => render( @@ -149,21 +156,19 @@ describe('', () => { expect(getByTestId(TITLE_LINK_TEST_ID)).toHaveTextContent('Prevalence'); - const iconDataTestSubj1 = `${PREVALENCE_TEST_ID}${field1}Icon`; - const valueDataTestSubj1 = `${PREVALENCE_TEST_ID}${field1}Value`; - expect(getByTestId(iconDataTestSubj1)).toBeInTheDocument(); - expect(getByTestId(valueDataTestSubj1)).toBeInTheDocument(); - expect(getByTestId(valueDataTestSubj1)).toHaveTextContent('field1, value1 is uncommon'); - - const iconDataTestSubj2 = `${PREVALENCE_TEST_ID}${field2}Icon`; - const valueDataTestSubj2 = `${PREVALENCE_TEST_ID}${field2}Value`; - expect(getByTestId(iconDataTestSubj2)).toBeInTheDocument(); - expect(getByTestId(valueDataTestSubj2)).toBeInTheDocument(); - expect(getByTestId(valueDataTestSubj2)).toHaveTextContent('field2, value2,value22 is uncommon'); - - const iconDataTestSubj3 = `${PREVALENCE_TEST_ID}${field3}Icon`; - const valueDataTestSubj3 = `${PREVALENCE_TEST_ID}${field3}Value`; - expect(queryByTestId(iconDataTestSubj3)).not.toBeInTheDocument(); + const textDataTestSubj1 = SUMMARY_ROW_TEXT_TEST_ID(`${PREVALENCE_TEST_ID}${field1}`); + const valueDataTestSubj1 = SUMMARY_ROW_VALUE_TEST_ID(`${PREVALENCE_TEST_ID}${field1}`); + expect(getByTestId(textDataTestSubj1)).toHaveTextContent('field1, value1'); + expect(getByTestId(valueDataTestSubj1)).toHaveTextContent('Uncommon'); + + const textDataTestSubj2 = SUMMARY_ROW_TEXT_TEST_ID(`${PREVALENCE_TEST_ID}${field2}`); + const valueDataTestSubj2 = SUMMARY_ROW_VALUE_TEST_ID(`${PREVALENCE_TEST_ID}${field2}`); + expect(getByTestId(textDataTestSubj2)).toHaveTextContent('field2, value2'); + expect(getByTestId(valueDataTestSubj2)).toHaveTextContent('Uncommon'); + + const textDataTestSubj3 = SUMMARY_ROW_TEXT_TEST_ID(`${PREVALENCE_TEST_ID}${field3}`); + const valueDataTestSubj3 = SUMMARY_ROW_VALUE_TEST_ID(`${PREVALENCE_TEST_ID}${field3}`); + expect(queryByTestId(textDataTestSubj3)).not.toBeInTheDocument(); expect(queryByTestId(valueDataTestSubj3)).not.toBeInTheDocument(); expect(queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument(); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/prevalence_overview.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/prevalence_overview.tsx index 96ee603607742..966df0293db77 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/prevalence_overview.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/prevalence_overview.tsx @@ -7,10 +7,10 @@ import type { FC } from 'react'; import React, { useCallback, useMemo } from 'react'; -import { EuiFlexGroup } from '@elastic/eui'; +import { EuiBadge, EuiFlexGroup } from '@elastic/eui'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; import { FormattedMessage } from '@kbn/i18n-react'; -import { ExpandablePanel } from '@kbn/security-solution-common'; +import { ExpandablePanel } from '../../../shared/components/expandable_panel'; import { usePrevalence } from '../../shared/hooks/use_prevalence'; import { PREVALENCE_TEST_ID } from './test_ids'; import { useDocumentDetailsContext } from '../../shared/context'; @@ -19,6 +19,13 @@ import { LeftPanelInsightsTab } from '../../left'; import { PREVALENCE_TAB_ID } from '../../left/components/prevalence_details'; import { InsightsSummaryRow } from './insights_summary_row'; +const UNCOMMON = ( + +); + const PERCENTAGE_THRESHOLD = 0.1; // we show the prevalence if its value is below 10% const DEFAULT_FROM = 'now-30d'; const DEFAULT_TO = 'now'; @@ -104,18 +111,17 @@ export const PrevalenceOverview: FC = () => { content={{ loading, error }} data-test-subj={PREVALENCE_TEST_ID} > - + {uncommonData.length > 0 ? ( uncommonData.map((d) => ( + <> + {d.field} + {','} {d.values.toString()} + } + value={{UNCOMMON}} data-test-subj={`${PREVALENCE_TEST_ID}${d.field}`} key={`${PREVALENCE_TEST_ID}${d.field}`} /> diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_ancestry.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_ancestry.test.tsx index 5aad641c6e400..38efe27b16ea9 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_ancestry.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_ancestry.test.tsx @@ -9,22 +9,32 @@ import React from 'react'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { render } from '@testing-library/react'; import { - SUMMARY_ROW_ICON_TEST_ID, - SUMMARY_ROW_VALUE_TEST_ID, + SUMMARY_ROW_TEXT_TEST_ID, SUMMARY_ROW_LOADING_TEST_ID, CORRELATIONS_RELATED_ALERTS_BY_ANCESTRY_TEST_ID, + SUMMARY_ROW_BUTTON_TEST_ID, } from './test_ids'; import { RelatedAlertsByAncestry } from './related_alerts_by_ancestry'; import { useFetchRelatedAlertsByAncestry } from '../../shared/hooks/use_fetch_related_alerts_by_ancestry'; +import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; +import { DocumentDetailsLeftPanelKey } from '../../shared/constants/panel_keys'; +import { LeftPanelInsightsTab } from '../../left'; +import { CORRELATIONS_TAB_ID } from '../../left/components/correlations_details'; +import { useDocumentDetailsContext } from '../../shared/context'; +jest.mock('@kbn/expandable-flyout'); +jest.mock('../../shared/context'); jest.mock('../../shared/hooks/use_fetch_related_alerts_by_ancestry'); +const mockOpenLeftPanel = jest.fn(); const documentId = 'documentId'; const indices = ['indices']; const scopeId = 'scopeId'; +const eventId = 'eventId'; +const indexName = 'indexName'; -const ICON_TEST_ID = SUMMARY_ROW_ICON_TEST_ID(CORRELATIONS_RELATED_ALERTS_BY_ANCESTRY_TEST_ID); -const VALUE_TEST_ID = SUMMARY_ROW_VALUE_TEST_ID(CORRELATIONS_RELATED_ALERTS_BY_ANCESTRY_TEST_ID); +const TEXT_TEST_ID = SUMMARY_ROW_TEXT_TEST_ID(CORRELATIONS_RELATED_ALERTS_BY_ANCESTRY_TEST_ID); +const BUTTON_TEST_ID = SUMMARY_ROW_BUTTON_TEST_ID(CORRELATIONS_RELATED_ALERTS_BY_ANCESTRY_TEST_ID); const LOADING_TEST_ID = SUMMARY_ROW_LOADING_TEST_ID( CORRELATIONS_RELATED_ALERTS_BY_ANCESTRY_TEST_ID ); @@ -37,34 +47,40 @@ const renderRelatedAlertsByAncestry = () => ); describe('', () => { - it('should render many related alerts correctly', () => { + beforeEach(() => { + jest.clearAllMocks(); + + (useDocumentDetailsContext as jest.Mock).mockReturnValue({ + eventId, + indexName, + scopeId, + isPreviewMode: false, + }); + (useExpandableFlyoutApi as jest.Mock).mockReturnValue({ openLeftPanel: mockOpenLeftPanel }); + }); + + it('should render single related alert correctly', () => { (useFetchRelatedAlertsByAncestry as jest.Mock).mockReturnValue({ loading: false, error: false, - dataCount: 2, + dataCount: 1, }); const { getByTestId } = renderRelatedAlertsByAncestry(); - expect(getByTestId(ICON_TEST_ID)).toBeInTheDocument(); - const value = getByTestId(VALUE_TEST_ID); - expect(value).toBeInTheDocument(); - expect(value).toHaveTextContent('2 alerts related by ancestry'); - expect(getByTestId(VALUE_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(TEXT_TEST_ID)).toHaveTextContent('Alert related by ancestry'); + expect(getByTestId(BUTTON_TEST_ID)).toHaveTextContent('1'); }); - it('should render single related alerts correctly', () => { + it('should render multiple related alerts correctly', () => { (useFetchRelatedAlertsByAncestry as jest.Mock).mockReturnValue({ loading: false, error: false, - dataCount: 1, + dataCount: 2, }); const { getByTestId } = renderRelatedAlertsByAncestry(); - expect(getByTestId(ICON_TEST_ID)).toBeInTheDocument(); - const value = getByTestId(VALUE_TEST_ID); - expect(value).toBeInTheDocument(); - expect(value).toHaveTextContent('1 alert related by ancestry'); - expect(getByTestId(VALUE_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(TEXT_TEST_ID)).toHaveTextContent('Alerts related by ancestry'); + expect(getByTestId(BUTTON_TEST_ID)).toHaveTextContent('2'); }); it('should render loading skeleton', () => { @@ -85,4 +101,28 @@ describe('', () => { const { container } = renderRelatedAlertsByAncestry(); expect(container).toBeEmptyDOMElement(); }); + + it('should open the expanded section to the correct tab when the number is clicked', () => { + (useFetchRelatedAlertsByAncestry as jest.Mock).mockReturnValue({ + loading: false, + error: false, + dataCount: 1, + }); + + const { getByTestId } = renderRelatedAlertsByAncestry(); + getByTestId(BUTTON_TEST_ID).click(); + + expect(mockOpenLeftPanel).toHaveBeenCalledWith({ + id: DocumentDetailsLeftPanelKey, + path: { + tab: LeftPanelInsightsTab, + subTab: CORRELATIONS_TAB_ID, + }, + params: { + id: eventId, + indexName, + scopeId, + }, + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_ancestry.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_ancestry.tsx index 2e628ba61a7be..4b225d5595883 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_ancestry.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_ancestry.tsx @@ -5,14 +5,13 @@ * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; +import { CORRELATIONS_TAB_ID } from '../../left/components/correlations_details'; import { useFetchRelatedAlertsByAncestry } from '../../shared/hooks/use_fetch_related_alerts_by_ancestry'; import { InsightsSummaryRow } from './insights_summary_row'; import { CORRELATIONS_RELATED_ALERTS_BY_ANCESTRY_TEST_ID } from './test_ids'; -const ICON = 'warning'; - export interface RelatedAlertsByAncestryProps { /** * Id of the document @@ -41,21 +40,25 @@ export const RelatedAlertsByAncestry: React.VFC = indices, scopeId, }); - const text = ( - + + const text = useMemo( + () => ( + + ), + [dataCount] ); return ( diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_same_source_event.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_same_source_event.test.tsx index d52d547397789..80e7c99a60917 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_same_source_event.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_same_source_event.test.tsx @@ -9,23 +9,33 @@ import React from 'react'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { render } from '@testing-library/react'; import { - SUMMARY_ROW_ICON_TEST_ID, - SUMMARY_ROW_VALUE_TEST_ID, + SUMMARY_ROW_TEXT_TEST_ID, SUMMARY_ROW_LOADING_TEST_ID, CORRELATIONS_RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEST_ID, + SUMMARY_ROW_BUTTON_TEST_ID, } from './test_ids'; import { useFetchRelatedAlertsBySameSourceEvent } from '../../shared/hooks/use_fetch_related_alerts_by_same_source_event'; import { RelatedAlertsBySameSourceEvent } from './related_alerts_by_same_source_event'; +import { DocumentDetailsLeftPanelKey } from '../../shared/constants/panel_keys'; +import { LeftPanelInsightsTab } from '../../left'; +import { CORRELATIONS_TAB_ID } from '../../left/components/correlations_details'; +import { useDocumentDetailsContext } from '../../shared/context'; +import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; +jest.mock('@kbn/expandable-flyout'); +jest.mock('../../shared/context'); jest.mock('../../shared/hooks/use_fetch_related_alerts_by_same_source_event'); +const mockOpenLeftPanel = jest.fn(); const originalEventId = 'originalEventId'; const scopeId = 'scopeId'; +const eventId = 'eventId'; +const indexName = 'indexName'; -const ICON_TEST_ID = SUMMARY_ROW_ICON_TEST_ID( +const TEXT_TEST_ID = SUMMARY_ROW_TEXT_TEST_ID( CORRELATIONS_RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEST_ID ); -const VALUE_TEST_ID = SUMMARY_ROW_VALUE_TEST_ID( +const BUTTON_TEST_ID = SUMMARY_ROW_BUTTON_TEST_ID( CORRELATIONS_RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEST_ID ); const LOADING_TEST_ID = SUMMARY_ROW_LOADING_TEST_ID( @@ -40,34 +50,40 @@ const renderRelatedAlertsBySameSourceEvent = () => ); describe('', () => { - it('should render many related alerts correctly', () => { + beforeEach(() => { + jest.clearAllMocks(); + + (useDocumentDetailsContext as jest.Mock).mockReturnValue({ + eventId, + indexName, + scopeId, + isPreviewMode: false, + }); + (useExpandableFlyoutApi as jest.Mock).mockReturnValue({ openLeftPanel: mockOpenLeftPanel }); + }); + + it('should render single related alert correctly', () => { (useFetchRelatedAlertsBySameSourceEvent as jest.Mock).mockReturnValue({ loading: false, error: false, - dataCount: 2, + dataCount: 1, }); const { getByTestId } = renderRelatedAlertsBySameSourceEvent(); - expect(getByTestId(ICON_TEST_ID)).toBeInTheDocument(); - const value = getByTestId(VALUE_TEST_ID); - expect(value).toBeInTheDocument(); - expect(value).toHaveTextContent('2 alerts related by source event'); - expect(getByTestId(VALUE_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(TEXT_TEST_ID)).toHaveTextContent('Alert related by source event'); + expect(getByTestId(BUTTON_TEST_ID)).toHaveTextContent('1'); }); - it('should render single related alerts correctly', () => { + it('should render multiple related alerts correctly', () => { (useFetchRelatedAlertsBySameSourceEvent as jest.Mock).mockReturnValue({ loading: false, error: false, - dataCount: 1, + dataCount: 2, }); const { getByTestId } = renderRelatedAlertsBySameSourceEvent(); - expect(getByTestId(ICON_TEST_ID)).toBeInTheDocument(); - const value = getByTestId(VALUE_TEST_ID); - expect(value).toBeInTheDocument(); - expect(value).toHaveTextContent('1 alert related by source event'); - expect(getByTestId(VALUE_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(TEXT_TEST_ID)).toHaveTextContent('Alerts related by source event'); + expect(getByTestId(BUTTON_TEST_ID)).toHaveTextContent('2'); }); it('should render loading skeleton', () => { @@ -87,10 +103,31 @@ describe('', () => { }); const { getByTestId } = renderRelatedAlertsBySameSourceEvent(); - expect(getByTestId(ICON_TEST_ID)).toBeInTheDocument(); - const value = getByTestId(VALUE_TEST_ID); - expect(value).toBeInTheDocument(); - expect(value).toHaveTextContent('0 alerts related by source event'); - expect(getByTestId(VALUE_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(TEXT_TEST_ID)).toHaveTextContent('Alerts related by source event'); + expect(getByTestId(BUTTON_TEST_ID)).toHaveTextContent('0'); + }); + + it('should open the expanded section to the correct tab when the number is clicked', () => { + (useFetchRelatedAlertsBySameSourceEvent as jest.Mock).mockReturnValue({ + loading: false, + error: true, + dataCount: 1, + }); + + const { getByTestId } = renderRelatedAlertsBySameSourceEvent(); + getByTestId(BUTTON_TEST_ID).click(); + + expect(mockOpenLeftPanel).toHaveBeenCalledWith({ + id: DocumentDetailsLeftPanelKey, + path: { + tab: LeftPanelInsightsTab, + subTab: CORRELATIONS_TAB_ID, + }, + params: { + id: eventId, + indexName, + scopeId, + }, + }); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_same_source_event.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_same_source_event.tsx index 0c1550dbb8692..dade35ca75546 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_same_source_event.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_same_source_event.tsx @@ -5,13 +5,12 @@ * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { useFetchRelatedAlertsBySameSourceEvent } from '../../shared/hooks/use_fetch_related_alerts_by_same_source_event'; -import { InsightsSummaryRow } from './insights_summary_row'; +import { CORRELATIONS_TAB_ID } from '../../left/components/correlations_details'; import { CORRELATIONS_RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEST_ID } from './test_ids'; - -const ICON = 'warning'; +import { InsightsSummaryRow } from './insights_summary_row'; +import { useFetchRelatedAlertsBySameSourceEvent } from '../../shared/hooks/use_fetch_related_alerts_by_same_source_event'; export interface RelatedAlertsBySameSourceEventProps { /** @@ -35,20 +34,24 @@ export const RelatedAlertsBySameSourceEvent: React.VFC + + const text = useMemo( + () => ( + + ), + [dataCount] ); return ( diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_session.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_session.test.tsx index 96ab397229420..4aeeef1feb8b1 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_session.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_session.test.tsx @@ -9,21 +9,31 @@ import React from 'react'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { render } from '@testing-library/react'; import { - SUMMARY_ROW_ICON_TEST_ID, - SUMMARY_ROW_VALUE_TEST_ID, + SUMMARY_ROW_TEXT_TEST_ID, SUMMARY_ROW_LOADING_TEST_ID, CORRELATIONS_RELATED_ALERTS_BY_SESSION_TEST_ID, + SUMMARY_ROW_BUTTON_TEST_ID, } from './test_ids'; import { RelatedAlertsBySession } from './related_alerts_by_session'; import { useFetchRelatedAlertsBySession } from '../../shared/hooks/use_fetch_related_alerts_by_session'; +import { DocumentDetailsLeftPanelKey } from '../../shared/constants/panel_keys'; +import { LeftPanelInsightsTab } from '../../left'; +import { CORRELATIONS_TAB_ID } from '../../left/components/correlations_details'; +import { useDocumentDetailsContext } from '../../shared/context'; +import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; +jest.mock('@kbn/expandable-flyout'); +jest.mock('../../shared/context'); jest.mock('../../shared/hooks/use_fetch_related_alerts_by_session'); +const mockOpenLeftPanel = jest.fn(); +const eventId = 'eventId'; +const indexName = 'indexName'; const entityId = 'entityId'; const scopeId = 'scopeId'; -const ICON_TEST_ID = SUMMARY_ROW_ICON_TEST_ID(CORRELATIONS_RELATED_ALERTS_BY_SESSION_TEST_ID); -const VALUE_TEST_ID = SUMMARY_ROW_VALUE_TEST_ID(CORRELATIONS_RELATED_ALERTS_BY_SESSION_TEST_ID); +const TEXT_TEST_ID = SUMMARY_ROW_TEXT_TEST_ID(CORRELATIONS_RELATED_ALERTS_BY_SESSION_TEST_ID); +const BUTTON_TEST_ID = SUMMARY_ROW_BUTTON_TEST_ID(CORRELATIONS_RELATED_ALERTS_BY_SESSION_TEST_ID); const LOADING_TEST_ID = SUMMARY_ROW_LOADING_TEST_ID(CORRELATIONS_RELATED_ALERTS_BY_SESSION_TEST_ID); const renderRelatedAlertsBySession = () => @@ -34,34 +44,40 @@ const renderRelatedAlertsBySession = () => ); describe('', () => { - it('should render many related alerts correctly', () => { + beforeEach(() => { + jest.clearAllMocks(); + + (useDocumentDetailsContext as jest.Mock).mockReturnValue({ + eventId, + indexName, + scopeId, + isPreviewMode: false, + }); + (useExpandableFlyoutApi as jest.Mock).mockReturnValue({ openLeftPanel: mockOpenLeftPanel }); + }); + + it('should render single related alerts correctly', () => { (useFetchRelatedAlertsBySession as jest.Mock).mockReturnValue({ loading: false, error: false, - dataCount: 2, + dataCount: 1, }); const { getByTestId } = renderRelatedAlertsBySession(); - expect(getByTestId(ICON_TEST_ID)).toBeInTheDocument(); - const value = getByTestId(VALUE_TEST_ID); - expect(value).toBeInTheDocument(); - expect(value).toHaveTextContent('2 alerts related by session'); - expect(getByTestId(VALUE_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(TEXT_TEST_ID)).toHaveTextContent('Alert related by session'); + expect(getByTestId(BUTTON_TEST_ID)).toHaveTextContent('1'); }); - it('should render single related alerts correctly', () => { + it('should render multiple related alerts correctly', () => { (useFetchRelatedAlertsBySession as jest.Mock).mockReturnValue({ loading: false, error: false, - dataCount: 1, + dataCount: 2, }); const { getByTestId } = renderRelatedAlertsBySession(); - expect(getByTestId(ICON_TEST_ID)).toBeInTheDocument(); - const value = getByTestId(VALUE_TEST_ID); - expect(value).toBeInTheDocument(); - expect(value).toHaveTextContent('1 alert related by session'); - expect(getByTestId(VALUE_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(TEXT_TEST_ID)).toHaveTextContent('Alerts related by session'); + expect(getByTestId(BUTTON_TEST_ID)).toHaveTextContent('2'); }); it('should render loading skeleton', () => { @@ -82,4 +98,28 @@ describe('', () => { const { container } = renderRelatedAlertsBySession(); expect(container).toBeEmptyDOMElement(); }); + + it('should open the expanded section to the correct tab when the number is clicked', () => { + (useFetchRelatedAlertsBySession as jest.Mock).mockReturnValue({ + loading: false, + error: false, + dataCount: 1, + }); + + const { getByTestId } = renderRelatedAlertsBySession(); + getByTestId(BUTTON_TEST_ID).click(); + + expect(mockOpenLeftPanel).toHaveBeenCalledWith({ + id: DocumentDetailsLeftPanelKey, + path: { + tab: LeftPanelInsightsTab, + subTab: CORRELATIONS_TAB_ID, + }, + params: { + id: eventId, + indexName, + scopeId, + }, + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_session.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_session.tsx index 4b41389137fad..9037ebca232a0 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_session.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_alerts_by_session.tsx @@ -5,14 +5,13 @@ * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; +import { CORRELATIONS_TAB_ID } from '../../left/components/correlations_details'; import { useFetchRelatedAlertsBySession } from '../../shared/hooks/use_fetch_related_alerts_by_session'; import { InsightsSummaryRow } from './insights_summary_row'; import { CORRELATIONS_RELATED_ALERTS_BY_SESSION_TEST_ID } from './test_ids'; -const ICON = 'warning'; - export interface RelatedAlertsBySessionProps { /** * Value of the process.entry_leader.entity_id field @@ -35,21 +34,25 @@ export const RelatedAlertsBySession: React.VFC = ({ entityId, scopeId, }); - const text = ( - + + const text = useMemo( + () => ( + + ), + [dataCount] ); return ( diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_cases.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_cases.test.tsx index 3d20e6399af38..e55d0e109d1d7 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_cases.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_cases.test.tsx @@ -10,19 +10,29 @@ import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { render } from '@testing-library/react'; import { CORRELATIONS_RELATED_CASES_TEST_ID, - SUMMARY_ROW_ICON_TEST_ID, + SUMMARY_ROW_TEXT_TEST_ID, SUMMARY_ROW_LOADING_TEST_ID, - SUMMARY_ROW_VALUE_TEST_ID, + SUMMARY_ROW_BUTTON_TEST_ID, } from './test_ids'; import { RelatedCases } from './related_cases'; import { useFetchRelatedCases } from '../../shared/hooks/use_fetch_related_cases'; +import { DocumentDetailsLeftPanelKey } from '../../shared/constants/panel_keys'; +import { LeftPanelInsightsTab } from '../../left'; +import { CORRELATIONS_TAB_ID } from '../../left/components/correlations_details'; +import { useDocumentDetailsContext } from '../../shared/context'; +import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; +jest.mock('@kbn/expandable-flyout'); +jest.mock('../../shared/context'); jest.mock('../../shared/hooks/use_fetch_related_cases'); +const mockOpenLeftPanel = jest.fn(); const eventId = 'eventId'; +const indexName = 'indexName'; +const scopeId = 'scopeId'; -const ICON_TEST_ID = SUMMARY_ROW_ICON_TEST_ID(CORRELATIONS_RELATED_CASES_TEST_ID); -const VALUE_TEST_ID = SUMMARY_ROW_VALUE_TEST_ID(CORRELATIONS_RELATED_CASES_TEST_ID); +const TEXT_TEST_ID = SUMMARY_ROW_TEXT_TEST_ID(CORRELATIONS_RELATED_CASES_TEST_ID); +const BUTTON_TEST_ID = SUMMARY_ROW_BUTTON_TEST_ID(CORRELATIONS_RELATED_CASES_TEST_ID); const LOADING_TEST_ID = SUMMARY_ROW_LOADING_TEST_ID(CORRELATIONS_RELATED_CASES_TEST_ID); const renderRelatedCases = () => @@ -33,34 +43,40 @@ const renderRelatedCases = () => ); describe('', () => { - it('should render many related cases correctly', () => { + beforeEach(() => { + jest.clearAllMocks(); + + (useDocumentDetailsContext as jest.Mock).mockReturnValue({ + eventId, + indexName, + scopeId, + isPreviewMode: false, + }); + (useExpandableFlyoutApi as jest.Mock).mockReturnValue({ openLeftPanel: mockOpenLeftPanel }); + }); + + it('should render single related case correctly', () => { (useFetchRelatedCases as jest.Mock).mockReturnValue({ loading: false, error: false, - dataCount: 2, + dataCount: 1, }); const { getByTestId } = renderRelatedCases(); - expect(getByTestId(ICON_TEST_ID)).toBeInTheDocument(); - const value = getByTestId(VALUE_TEST_ID); - expect(value).toBeInTheDocument(); - expect(value).toHaveTextContent('2 related cases'); - expect(getByTestId(VALUE_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(TEXT_TEST_ID)).toHaveTextContent('Related case'); + expect(getByTestId(BUTTON_TEST_ID)).toHaveTextContent('1'); }); - it('should render single related case correctly', () => { + it('should render multiple related cases correctly', () => { (useFetchRelatedCases as jest.Mock).mockReturnValue({ loading: false, error: false, - dataCount: 1, + dataCount: 2, }); const { getByTestId } = renderRelatedCases(); - expect(getByTestId(ICON_TEST_ID)).toBeInTheDocument(); - const value = getByTestId(VALUE_TEST_ID); - expect(value).toBeInTheDocument(); - expect(value).toHaveTextContent('1 related case'); - expect(getByTestId(VALUE_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(TEXT_TEST_ID)).toHaveTextContent('Related cases'); + expect(getByTestId(BUTTON_TEST_ID)).toHaveTextContent('2'); }); it('should render loading skeleton', () => { @@ -81,4 +97,28 @@ describe('', () => { const { container } = renderRelatedCases(); expect(container).toBeEmptyDOMElement(); }); + + it('should open the expanded section to the correct tab when the number is clicked', () => { + (useFetchRelatedCases as jest.Mock).mockReturnValue({ + loading: false, + error: false, + dataCount: 1, + }); + + const { getByTestId } = renderRelatedCases(); + getByTestId(BUTTON_TEST_ID).click(); + + expect(mockOpenLeftPanel).toHaveBeenCalledWith({ + id: DocumentDetailsLeftPanelKey, + path: { + tab: LeftPanelInsightsTab, + subTab: CORRELATIONS_TAB_ID, + }, + params: { + id: eventId, + indexName, + scopeId, + }, + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_cases.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_cases.tsx index d45cc971dc046..8a01b21799d86 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_cases.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/related_cases.tsx @@ -5,13 +5,12 @@ * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { useFetchRelatedCases } from '../../shared/hooks/use_fetch_related_cases'; -import { InsightsSummaryRow } from './insights_summary_row'; +import { CORRELATIONS_TAB_ID } from '../../left/components/correlations_details'; import { CORRELATIONS_RELATED_CASES_TEST_ID } from './test_ids'; - -const ICON = 'warning'; +import { InsightsSummaryRow } from './insights_summary_row'; +import { useFetchRelatedCases } from '../../shared/hooks/use_fetch_related_cases'; export interface RelatedCasesProps { /** @@ -21,25 +20,29 @@ export interface RelatedCasesProps { } /** - * + * Show related cases in summary row */ export const RelatedCases: React.VFC = ({ eventId }) => { const { loading, error, dataCount } = useFetchRelatedCases({ eventId }); - const text = ( - + + const text = useMemo( + () => ( + + ), + [dataCount] ); 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 db7f60938c0c3..9ccb512030b43 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 @@ -19,7 +19,7 @@ import { EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID, EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID, EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID, -} from '@kbn/security-solution-common'; +} from '../../../shared/components/test_ids'; import { mockContextValue } from '../../shared/mocks/mock_context'; jest.mock('../hooks/use_session_preview'); 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 974c74b995393..4098b35a0abfc 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 @@ -9,7 +9,6 @@ import React, { type FC, useCallback } from 'react'; import { TimelineTabs } from '@kbn/securitysolution-data-table'; import { useDispatch } from 'react-redux'; import { FormattedMessage } from '@kbn/i18n-react'; -import { ExpandablePanel } from '@kbn/security-solution-common'; import { useUiSetting$ } from '@kbn/kibana-react-plugin/public'; import { ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING } from '../../../../../common/constants'; import { useLicense } from '../../../../common/hooks/use_license'; @@ -18,6 +17,7 @@ import { useSessionPreview } from '../hooks/use_session_preview'; import { useInvestigateInTimeline } from '../../../../detections/components/alerts_table/timeline_actions/use_investigate_in_timeline'; import { useDocumentDetailsContext } from '../../shared/context'; import { ALERTS_ACTIONS } from '../../../../common/lib/apm/user_actions'; +import { ExpandablePanel } from '../../../shared/components/expandable_panel'; import { SESSION_PREVIEW_TEST_ID } from './test_ids'; import { useStartTransaction } from '../../../../common/lib/apm/use_start_transaction'; import { setActiveTabTimeline } from '../../../../timelines/store/actions'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/suppressed_alerts.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/suppressed_alerts.test.tsx index b5954c251c014..331283e194ed0 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/suppressed_alerts.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/suppressed_alerts.test.tsx @@ -9,16 +9,27 @@ import React from 'react'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { render } from '@testing-library/react'; import { - SUMMARY_ROW_ICON_TEST_ID, - SUMMARY_ROW_VALUE_TEST_ID, + SUMMARY_ROW_TEXT_TEST_ID, CORRELATIONS_SUPPRESSED_ALERTS_TEST_ID, CORRELATIONS_SUPPRESSED_ALERTS_TECHNICAL_PREVIEW_TEST_ID, + SUMMARY_ROW_BUTTON_TEST_ID, } from './test_ids'; import { SuppressedAlerts } from './suppressed_alerts'; import { isSuppressionRuleInGA } from '../../../../../common/detection_engine/utils'; +import { DocumentDetailsLeftPanelKey } from '../../shared/constants/panel_keys'; +import { LeftPanelInsightsTab } from '../../left'; +import { CORRELATIONS_TAB_ID } from '../../left/components/correlations_details'; +import { useDocumentDetailsContext } from '../../shared/context'; +import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -const ICON_TEST_ID = SUMMARY_ROW_ICON_TEST_ID(CORRELATIONS_SUPPRESSED_ALERTS_TEST_ID); -const VALUE_TEST_ID = SUMMARY_ROW_VALUE_TEST_ID(CORRELATIONS_SUPPRESSED_ALERTS_TEST_ID); +jest.mock('@kbn/expandable-flyout'); +jest.mock('../../shared/context'); +jest.mock('../../../../../common/detection_engine/utils', () => ({ + isSuppressionRuleInGA: jest.fn().mockReturnValue(false), +})); + +const TEXT_TEST_ID = SUMMARY_ROW_TEXT_TEST_ID(CORRELATIONS_SUPPRESSED_ALERTS_TEST_ID); +const BUTTON_TEST_ID = SUMMARY_ROW_BUTTON_TEST_ID(CORRELATIONS_SUPPRESSED_ALERTS_TEST_ID); const renderSuppressedAlerts = (alertSuppressionCount: number) => render( @@ -27,34 +38,30 @@ const renderSuppressedAlerts = (alertSuppressionCount: number) => ); -jest.mock('../../../../../common/detection_engine/utils', () => ({ - isSuppressionRuleInGA: jest.fn().mockReturnValue(false), -})); - +const mockOpenLeftPanel = jest.fn(); +const scopeId = 'scopeId'; +const eventId = 'eventId'; +const indexName = 'indexName'; const isSuppressionRuleInGAMock = isSuppressionRuleInGA as jest.Mock; describe('', () => { - it('should render zero suppressed alert correctly', () => { - const { getByTestId } = renderSuppressedAlerts(0); + beforeEach(() => { + jest.clearAllMocks(); - expect(getByTestId(ICON_TEST_ID)).toBeInTheDocument(); - const value = getByTestId(VALUE_TEST_ID); - expect(value).toBeInTheDocument(); - expect(value).toHaveTextContent('0 suppressed alert'); - expect(getByTestId(VALUE_TEST_ID)).toBeInTheDocument(); - expect( - getByTestId(CORRELATIONS_SUPPRESSED_ALERTS_TECHNICAL_PREVIEW_TEST_ID) - ).toBeInTheDocument(); + (useDocumentDetailsContext as jest.Mock).mockReturnValue({ + eventId, + indexName, + scopeId, + isPreviewMode: false, + }); + (useExpandableFlyoutApi as jest.Mock).mockReturnValue({ openLeftPanel: mockOpenLeftPanel }); }); it('should render single suppressed alert correctly', () => { const { getByTestId } = renderSuppressedAlerts(1); - expect(getByTestId(ICON_TEST_ID)).toBeInTheDocument(); - const value = getByTestId(VALUE_TEST_ID); - expect(value).toBeInTheDocument(); - expect(value).toHaveTextContent('1 suppressed alert'); - expect(getByTestId(VALUE_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(TEXT_TEST_ID)).toHaveTextContent('Suppressed alert'); + expect(getByTestId(BUTTON_TEST_ID)).toHaveTextContent('1'); expect( getByTestId(CORRELATIONS_SUPPRESSED_ALERTS_TECHNICAL_PREVIEW_TEST_ID) ).toBeInTheDocument(); @@ -63,14 +70,8 @@ describe('', () => { it('should render multiple suppressed alerts row correctly', () => { const { getByTestId } = renderSuppressedAlerts(2); - expect(getByTestId(ICON_TEST_ID)).toBeInTheDocument(); - const value = getByTestId(VALUE_TEST_ID); - expect(value).toBeInTheDocument(); - expect(value).toHaveTextContent('2 suppressed alerts'); - expect(getByTestId(VALUE_TEST_ID)).toBeInTheDocument(); - expect( - getByTestId(CORRELATIONS_SUPPRESSED_ALERTS_TECHNICAL_PREVIEW_TEST_ID) - ).toBeInTheDocument(); + expect(getByTestId(TEXT_TEST_ID)).toHaveTextContent('Suppressed alerts'); + expect(getByTestId(BUTTON_TEST_ID)).toHaveTextContent('2'); }); it('should not render Technical Preview badge if rule type is in GA', () => { @@ -81,4 +82,22 @@ describe('', () => { queryByTestId(CORRELATIONS_SUPPRESSED_ALERTS_TECHNICAL_PREVIEW_TEST_ID) ).not.toBeInTheDocument(); }); + + it('should open the expanded section to the correct tab when the number is clicked', () => { + const { getByTestId } = renderSuppressedAlerts(1); + getByTestId(BUTTON_TEST_ID).click(); + + expect(mockOpenLeftPanel).toHaveBeenCalledWith({ + id: DocumentDetailsLeftPanelKey, + path: { + tab: LeftPanelInsightsTab, + subTab: CORRELATIONS_TAB_ID, + }, + params: { + id: eventId, + indexName, + scopeId, + }, + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/suppressed_alerts.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/suppressed_alerts.tsx index a8cd147a4ac14..c7eb50aeb383a 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/suppressed_alerts.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/suppressed_alerts.tsx @@ -5,18 +5,19 @@ * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiBetaBadge } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import type { Type } from '@kbn/securitysolution-io-ts-alerting-types'; import { i18n } from '@kbn/i18n'; -import { isSuppressionRuleInGA } from '../../../../../common/detection_engine/utils'; +import { CORRELATIONS_TAB_ID } from '../../left/components/correlations_details'; +import { InsightsSummaryRow } from './insights_summary_row'; import { CORRELATIONS_SUPPRESSED_ALERTS_TEST_ID, CORRELATIONS_SUPPRESSED_ALERTS_TECHNICAL_PREVIEW_TEST_ID, } from './test_ids'; -import { InsightsSummaryRow } from './insights_summary_row'; +import { isSuppressionRuleInGA } from '../../../../../common/detection_engine/utils'; const SUPPRESSED_ALERTS_COUNT_TECHNICAL_PREVIEW = i18n.translate( 'xpack.securitySolution.flyout.right.overview.insights.suppressedAlertsCountTechnicalPreview', @@ -43,21 +44,24 @@ export const SuppressedAlerts: React.VFC = ({ alertSuppressionCount, ruleType, }) => { + const text = useMemo( + () => ( + + ), + [alertSuppressionCount] + ); + return ( - } + expandedSubTab={CORRELATIONS_TAB_ID} data-test-subj={CORRELATIONS_SUPPRESSED_ALERTS_TEST_ID} key={`correlation-row-suppressed-alerts`} /> diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts index e649c578bf487..959f8f106bb08 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts @@ -104,8 +104,9 @@ export const INSIGHTS_CONTENT_TEST_ID = INSIGHTS_TEST_ID + CONTENT_TEST_ID; /* Summary row */ export const SUMMARY_ROW_LOADING_TEST_ID = (dataTestSubj: string) => `${dataTestSubj}Loading`; -export const SUMMARY_ROW_ICON_TEST_ID = (dataTestSubj: string) => `${dataTestSubj}Icon`; +export const SUMMARY_ROW_TEXT_TEST_ID = (dataTestSubj: string) => `${dataTestSubj}Text`; export const SUMMARY_ROW_VALUE_TEST_ID = (dataTestSubj: string) => `${dataTestSubj}Value`; +export const SUMMARY_ROW_BUTTON_TEST_ID = (dataTestSubj: string) => `${dataTestSubj}Button`; /* Entities */ @@ -146,6 +147,10 @@ export const ENTITIES_HOST_OVERVIEW_VULNERABILITIES_TEST_ID = /* Threat intelligence */ export const INSIGHTS_THREAT_INTELLIGENCE_TEST_ID = `${PREFIX}InsightsThreatIntelligence` as const; +export const INSIGHTS_THREAT_INTELLIGENCE_THREAT_MATCHES_TEST_ID = + `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}ThreatMatches` as const; +export const INSIGHTS_THREAT_INTELLIGENCE_ENRICHED_WITH_THREAT_INTELLIGENCE_TEST_ID = + `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}EnrichedWithThreatIntelligence` as const; /* Correlations */ 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 af92283b781b5..1d19dacc39d42 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 @@ -6,25 +6,32 @@ */ import React from 'react'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { render } from '@testing-library/react'; -import { useExpandableFlyoutApi, type ExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import { DocumentDetailsContext } from '../../shared/context'; -import { TestProviders } from '../../../../common/mock'; +import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; +import { useDocumentDetailsContext } from '../../shared/context'; import { ThreatIntelligenceOverview } from './threat_intelligence_overview'; import { DocumentDetailsLeftPanelKey } from '../../shared/constants/panel_keys'; import { LeftPanelInsightsTab } from '../../left'; import { useFetchThreatIntelligence } from '../hooks/use_fetch_threat_intelligence'; import { THREAT_INTELLIGENCE_TAB_ID } from '../../left/components/threat_intelligence_details'; -import { INSIGHTS_THREAT_INTELLIGENCE_TEST_ID } from './test_ids'; import { - EXPANDABLE_PANEL_CONTENT_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_ENRICHED_WITH_THREAT_INTELLIGENCE_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_THREAT_MATCHES_TEST_ID, + SUMMARY_ROW_BUTTON_TEST_ID, + SUMMARY_ROW_TEXT_TEST_ID, +} from './test_ids'; +import { EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID, EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID, EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID, EXPANDABLE_PANEL_LOADING_TEST_ID, EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID, -} from '@kbn/security-solution-common'; +} from '../../../shared/components/test_ids'; +jest.mock('@kbn/expandable-flyout'); +jest.mock('../../shared/context'); jest.mock('../hooks/use_fetch_threat_intelligence'); const TOGGLE_ICON_TEST_ID = EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID( @@ -39,32 +46,45 @@ const TITLE_ICON_TEST_ID = EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID( const TITLE_TEXT_TEST_ID = EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID( INSIGHTS_THREAT_INTELLIGENCE_TEST_ID ); -const CONTENT_TEST_ID = EXPANDABLE_PANEL_CONTENT_TEST_ID(INSIGHTS_THREAT_INTELLIGENCE_TEST_ID); const LOADING_TEST_ID = EXPANDABLE_PANEL_LOADING_TEST_ID(INSIGHTS_THREAT_INTELLIGENCE_TEST_ID); +const THREAT_MATCHES_TEXT_TEST_ID = SUMMARY_ROW_TEXT_TEST_ID( + INSIGHTS_THREAT_INTELLIGENCE_THREAT_MATCHES_TEST_ID +); +const THREAT_MATCHES_BUTTON_TEST_ID = SUMMARY_ROW_BUTTON_TEST_ID( + INSIGHTS_THREAT_INTELLIGENCE_THREAT_MATCHES_TEST_ID +); +const ENRICHED_WITH_THREAT_INTELLIGENCE_TEXT_TEST_ID = SUMMARY_ROW_TEXT_TEST_ID( + INSIGHTS_THREAT_INTELLIGENCE_ENRICHED_WITH_THREAT_INTELLIGENCE_TEST_ID +); +const ENRICHED_WITH_THREAT_INTELLIGENCE_BUTTON_TEST_ID = SUMMARY_ROW_BUTTON_TEST_ID( + INSIGHTS_THREAT_INTELLIGENCE_ENRICHED_WITH_THREAT_INTELLIGENCE_TEST_ID +); -const panelContextValue = { - eventId: 'event id', - indexName: 'indexName', - dataFormattedForFieldBrowser: [], -} as unknown as DocumentDetailsContext; +const mockOpenLeftPanel = jest.fn(); +const eventId = 'eventId'; +const indexName = 'indexName'; +const scopeId = 'scopeId'; +const dataFormattedForFieldBrowser = ['scopeId']; -jest.mock('@kbn/expandable-flyout'); - -const renderThreatIntelligenceOverview = (contextValue: DocumentDetailsContext) => ( - - +const renderThreatIntelligenceOverview = () => + render( + - - -); - -const flyoutContextValue = { - openLeftPanel: jest.fn(), -} as unknown as ExpandableFlyoutApi; + + ); describe('', () => { - beforeAll(() => { - jest.mocked(useExpandableFlyoutApi).mockReturnValue(flyoutContextValue); + beforeEach(() => { + jest.clearAllMocks(); + + (useDocumentDetailsContext as jest.Mock).mockReturnValue({ + eventId, + indexName, + scopeId, + dataFormattedForFieldBrowser, + isPreviewMode: false, + }); + (useExpandableFlyoutApi as jest.Mock).mockReturnValue({ openLeftPanel: mockOpenLeftPanel }); }); it('should render wrapper component', () => { @@ -72,9 +92,7 @@ describe('', () => { loading: false, }); - const { getByTestId, queryByTestId } = render( - renderThreatIntelligenceOverview(panelContextValue) - ); + const { getByTestId, queryByTestId } = renderThreatIntelligenceOverview(); expect(queryByTestId(TOGGLE_ICON_TEST_ID)).not.toBeInTheDocument(); expect(getByTestId(TITLE_ICON_TEST_ID)).toBeInTheDocument(); @@ -82,14 +100,19 @@ describe('', () => { expect(queryByTestId(TITLE_TEXT_TEST_ID)).not.toBeInTheDocument(); }); - it('should not render link if isPrenviewMode is true', () => { + it('should not render link if isPreviewMode is true', () => { + (useDocumentDetailsContext as jest.Mock).mockReturnValue({ + eventId, + indexName, + scopeId, + dataFormattedForFieldBrowser, + isPreviewMode: true, + }); (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ loading: false, }); - const { getByTestId, queryByTestId } = render( - renderThreatIntelligenceOverview({ ...panelContextValue, isPreviewMode: true }) - ); + const { getByTestId, queryByTestId } = renderThreatIntelligenceOverview(); expect(queryByTestId(TOGGLE_ICON_TEST_ID)).not.toBeInTheDocument(); expect(queryByTestId(TITLE_ICON_TEST_ID)).not.toBeInTheDocument(); @@ -104,13 +127,15 @@ describe('', () => { threatEnrichmentsCount: 1, }); - const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); + const { getByTestId } = renderThreatIntelligenceOverview(); expect(getByTestId(TITLE_LINK_TEST_ID)).toHaveTextContent('Threat intelligence'); - expect(getByTestId(CONTENT_TEST_ID)).toHaveTextContent('1 threat match detected'); - expect(getByTestId(CONTENT_TEST_ID)).toHaveTextContent( - '1 field enriched with threat intelligence' + expect(getByTestId(THREAT_MATCHES_TEXT_TEST_ID)).toHaveTextContent('Threat match detected'); + expect(getByTestId(THREAT_MATCHES_BUTTON_TEST_ID)).toHaveTextContent('1'); + expect(getByTestId(ENRICHED_WITH_THREAT_INTELLIGENCE_TEXT_TEST_ID)).toHaveTextContent( + 'Field enriched with threat intelligence' ); + expect(getByTestId(ENRICHED_WITH_THREAT_INTELLIGENCE_BUTTON_TEST_ID)).toHaveTextContent('1'); }); it('should render 2 matches detected and 2 fields enriched', () => { @@ -120,72 +145,85 @@ describe('', () => { threatEnrichmentsCount: 2, }); - const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); + const { getByTestId } = renderThreatIntelligenceOverview(); expect(getByTestId(TITLE_LINK_TEST_ID)).toHaveTextContent('Threat intelligence'); - expect(getByTestId(CONTENT_TEST_ID)).toHaveTextContent('2 threat matches detected'); - expect(getByTestId(CONTENT_TEST_ID)).toHaveTextContent( - '2 fields enriched with threat intelligence' + expect(getByTestId(THREAT_MATCHES_TEXT_TEST_ID)).toHaveTextContent('Threat matches detected'); + expect(getByTestId(THREAT_MATCHES_BUTTON_TEST_ID)).toHaveTextContent('2'); + expect(getByTestId(ENRICHED_WITH_THREAT_INTELLIGENCE_TEXT_TEST_ID)).toHaveTextContent( + 'Fields enriched with threat intelligence' ); + expect(getByTestId(ENRICHED_WITH_THREAT_INTELLIGENCE_BUTTON_TEST_ID)).toHaveTextContent('2'); }); - it('should render 0 fields enriched', () => { + it('should render loading', () => { (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ - loading: false, - threatMatchesCount: 1, - threatEnrichmentsCount: 0, + loading: true, }); - const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); + const { getByTestId } = renderThreatIntelligenceOverview(); - expect(getByTestId(CONTENT_TEST_ID)).toHaveTextContent( - '0 fields enriched with threat intelligence' - ); + expect(getByTestId(LOADING_TEST_ID)).toBeInTheDocument(); }); - it('should render 0 matches detected', () => { + it('should navigate to left section Insights tab when clicking on button', () => { (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ loading: false, - threatMatchesCount: 0, - threatEnrichmentsCount: 2, + threatMatchesCount: 1, + threatEnrichmentsCount: 1, }); + const { getByTestId } = renderThreatIntelligenceOverview(); - const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); - - expect(getByTestId(CONTENT_TEST_ID)).toHaveTextContent('0 threat matches detected'); - }); - - it('should render loading', () => { - (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ - loading: true, + getByTestId(TITLE_LINK_TEST_ID).click(); + expect(mockOpenLeftPanel).toHaveBeenCalledWith({ + id: DocumentDetailsLeftPanelKey, + path: { + tab: LeftPanelInsightsTab, + subTab: THREAT_INTELLIGENCE_TAB_ID, + }, + params: { + id: eventId, + indexName, + scopeId, + }, }); - - const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); - - expect(getByTestId(LOADING_TEST_ID)).toBeInTheDocument(); }); - it('should navigate to left section Insights tab when clicking on button', () => { + it('should open the expanded section to the correct tab when the number is clicked', () => { (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ loading: false, threatMatchesCount: 1, threatEnrichmentsCount: 1, }); - const { getByTestId } = render( - - - - - - ); - getByTestId(TITLE_LINK_TEST_ID).click(); - expect(flyoutContextValue.openLeftPanel).toHaveBeenCalledWith({ + const { getByTestId } = renderThreatIntelligenceOverview(); + getByTestId(THREAT_MATCHES_BUTTON_TEST_ID).click(); + + expect(mockOpenLeftPanel).toHaveBeenCalledWith({ + id: DocumentDetailsLeftPanelKey, + path: { + tab: LeftPanelInsightsTab, + subTab: THREAT_INTELLIGENCE_TAB_ID, + }, + params: { + id: eventId, + indexName, + scopeId, + }, + }); + + getByTestId(ENRICHED_WITH_THREAT_INTELLIGENCE_BUTTON_TEST_ID).click(); + + expect(mockOpenLeftPanel).toHaveBeenCalledWith({ id: DocumentDetailsLeftPanelKey, - path: { tab: LeftPanelInsightsTab, subTab: THREAT_INTELLIGENCE_TAB_ID }, + path: { + tab: LeftPanelInsightsTab, + subTab: THREAT_INTELLIGENCE_TAB_ID, + }, params: { - id: panelContextValue.eventId, - indexName: panelContextValue.indexName, + id: eventId, + indexName, + scopeId, }, }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/threat_intelligence_overview.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/threat_intelligence_overview.tsx index 10b23ecfc2340..73dfc62520c32 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/threat_intelligence_overview.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/threat_intelligence_overview.tsx @@ -10,15 +10,32 @@ import React, { useCallback, useMemo } from 'react'; import { EuiFlexGroup } from '@elastic/eui'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; import { FormattedMessage } from '@kbn/i18n-react'; -import { ExpandablePanel } from '@kbn/security-solution-common'; +import { ExpandablePanel } from '../../../shared/components/expandable_panel'; import { useFetchThreatIntelligence } from '../hooks/use_fetch_threat_intelligence'; import { InsightsSummaryRow } from './insights_summary_row'; import { useDocumentDetailsContext } from '../../shared/context'; -import { INSIGHTS_THREAT_INTELLIGENCE_TEST_ID } from './test_ids'; +import { + INSIGHTS_THREAT_INTELLIGENCE_ENRICHED_WITH_THREAT_INTELLIGENCE_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_THREAT_MATCHES_TEST_ID, +} from './test_ids'; import { DocumentDetailsLeftPanelKey } from '../../shared/constants/panel_keys'; import { LeftPanelInsightsTab } from '../../left'; import { THREAT_INTELLIGENCE_TAB_ID } from '../../left/components/threat_intelligence_details'; +const TITLE = ( + +); +const TOOLTIP = ( + +); + /** * Threat intelligence section under Insights section, overview tab. * The component fetches the necessary data, then pass it down to the InsightsSubSection component for loading and error state, @@ -53,26 +70,38 @@ export const ThreatIntelligenceOverview: FC = () => { !isPreviewMode ? { callback: goToThreatIntelligenceTab, - tooltip: ( - - ), + tooltip: TOOLTIP, } : undefined, [isPreviewMode, goToThreatIntelligenceTab] ); + const threatMatchCountText = useMemo( + () => ( + + ), + [threatMatchesCount] + ); + + const threatEnrichmentsCountText = useMemo( + () => ( + + ), + [threatEnrichmentsCount] + ); + return ( - ), + title: TITLE, link, iconType: !isPreviewMode ? 'arrowStart' : undefined, }} @@ -81,32 +110,20 @@ export const ThreatIntelligenceOverview: FC = () => { > - } - data-test-subj={INSIGHTS_THREAT_INTELLIGENCE_TEST_ID} + expandedSubTab={THREAT_INTELLIGENCE_TAB_ID} + data-test-subj={INSIGHTS_THREAT_INTELLIGENCE_THREAT_MATCHES_TEST_ID} /> - } - data-test-subj={INSIGHTS_THREAT_INTELLIGENCE_TEST_ID} + expandedSubTab={THREAT_INTELLIGENCE_TAB_ID} + data-test-subj={INSIGHTS_THREAT_INTELLIGENCE_ENRICHED_WITH_THREAT_INTELLIGENCE_TEST_ID} /> diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/tour.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/tour.test.tsx deleted file mode 100644 index 20540184156b9..0000000000000 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/tour.test.tsx +++ /dev/null @@ -1,125 +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, waitFor, fireEvent } from '@testing-library/react'; -import { RightPanelTour } from './tour'; -import { DocumentDetailsContext } from '../../shared/context'; -import { mockContextValue } from '../../shared/mocks/mock_context'; -import { - createMockStore, - createSecuritySolutionStorageMock, - TestProviders, -} from '../../../../common/mock'; -import { useKibana as mockUseKibana } from '../../../../common/lib/kibana/__mocks__'; -import { useKibana } from '../../../../common/lib/kibana'; -import { FLYOUT_TOUR_CONFIG_ANCHORS } from '../../shared/utils/tour_step_config'; -import { FLYOUT_TOUR_TEST_ID } from '../../shared/components/test_ids'; -import { useTourContext } from '../../../../common/components/guided_onboarding_tour/tour'; -import { casesPluginMock } from '@kbn/cases-plugin/public/mocks'; -import { useWhichFlyout } from '../../shared/hooks/use_which_flyout'; -import { Flyouts } from '../../shared/constants/flyouts'; - -jest.mock('../../../../common/lib/kibana'); -jest.mock('../../shared/hooks/use_which_flyout'); -jest.mock('../../../../common/components/guided_onboarding_tour/tour'); - -const mockedUseKibana = mockUseKibana(); - -const { storage: storageMock } = createSecuritySolutionStorageMock(); -const mockStore = createMockStore(undefined, undefined, undefined, { - ...storageMock, -}); -const mockCasesContract = casesPluginMock.createStartContract(); -const mockUseIsAddToCaseOpen = mockCasesContract.hooks.useIsAddToCaseOpen as jest.Mock; -mockUseIsAddToCaseOpen.mockReturnValue(false); - -const renderRightPanelTour = (context: DocumentDetailsContext = mockContextValue) => - render( - - - - {Object.values(FLYOUT_TOUR_CONFIG_ANCHORS).map((i, idx) => ( -
- ))} - - - ); - -describe('', () => { - beforeEach(() => { - (useKibana as jest.Mock).mockReturnValue({ - ...mockedUseKibana, - services: { - ...mockedUseKibana.services, - storage: storageMock, - cases: mockCasesContract, - }, - }); - (useWhichFlyout as jest.Mock).mockReturnValue(Flyouts.securitySolution); - (useTourContext as jest.Mock).mockReturnValue({ isTourShown: jest.fn(() => false) }); - storageMock.clear(); - }); - - it('should render tour for alerts', async () => { - const { getByText, getByTestId } = renderRightPanelTour(); - await waitFor(() => { - expect(getByTestId(`${FLYOUT_TOUR_TEST_ID}-1`)).toBeVisible(); - }); - fireEvent.click(getByText('Next')); - await waitFor(() => { - expect(getByTestId(`${FLYOUT_TOUR_TEST_ID}-2`)).toBeVisible(); - }); - fireEvent.click(getByText('Next')); - await waitFor(() => { - expect(getByTestId(`${FLYOUT_TOUR_TEST_ID}-3`)).toBeVisible(); - }); - fireEvent.click(getByText('Next')); - }); - - it('should not render tour for preview', () => { - const { queryByTestId, queryByText } = renderRightPanelTour({ - ...mockContextValue, - isPreview: true, - }); - expect(queryByTestId(`${FLYOUT_TOUR_TEST_ID}-1`)).not.toBeInTheDocument(); - expect(queryByText('Next')).not.toBeInTheDocument(); - }); - - it('should not render tour when guided onboarding tour is active', () => { - (useTourContext as jest.Mock).mockReturnValue({ isTourShown: jest.fn(() => true) }); - const { queryByText, queryByTestId } = renderRightPanelTour({ - ...mockContextValue, - getFieldsData: () => '', - }); - - expect(queryByTestId(`${FLYOUT_TOUR_TEST_ID}-1`)).not.toBeInTheDocument(); - expect(queryByText('Next')).not.toBeInTheDocument(); - }); - - it('should not render tour when case modal is open', () => { - mockUseIsAddToCaseOpen.mockReturnValue(true); - const { queryByText, queryByTestId } = renderRightPanelTour({ - ...mockContextValue, - getFieldsData: () => '', - }); - - expect(queryByTestId(`${FLYOUT_TOUR_TEST_ID}-1`)).not.toBeInTheDocument(); - expect(queryByText('Next')).not.toBeInTheDocument(); - }); - - it('should not render tour for flyout in timeline', () => { - (useWhichFlyout as jest.Mock).mockReturnValue(Flyouts.timeline); - const { queryByText, queryByTestId } = renderRightPanelTour({ - ...mockContextValue, - getFieldsData: () => '', - }); - - expect(queryByTestId(`${FLYOUT_TOUR_TEST_ID}-1`)).not.toBeInTheDocument(); - expect(queryByText('Next')).not.toBeInTheDocument(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/tour.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/tour.tsx deleted file mode 100644 index 839b77766886a..0000000000000 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/tour.tsx +++ /dev/null @@ -1,90 +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, { memo, useMemo, useCallback } from 'react'; -import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import { useWhichFlyout } from '../../shared/hooks/use_which_flyout'; -import { Flyouts } from '../../shared/constants/flyouts'; -import { useDocumentDetailsContext } from '../../shared/context'; -import { - getRightSectionTourSteps, - getLeftSectionTourSteps, -} from '../../shared/utils/tour_step_config'; -import { getField } from '../../shared/utils'; -import { - DocumentDetailsLeftPanelKey, - DocumentDetailsRightPanelKey, -} from '../../shared/constants/panel_keys'; -import { EventKind } from '../../shared/constants/event_kinds'; -import { useTourContext } from '../../../../common/components/guided_onboarding_tour/tour'; -import { SecurityStepId } from '../../../../common/components/guided_onboarding_tour/tour_config'; -import { useKibana } from '../../../../common/lib/kibana'; -import { FlyoutTour } from '../../shared/components/flyout_tour'; - -/** - * Guided tour for the right panel in details flyout - */ -export const RightPanelTour = memo(() => { - const { useIsAddToCaseOpen } = useKibana().services.cases.hooks; - - const casesFlyoutExpanded = useIsAddToCaseOpen(); - - const { isTourShown: isGuidedOnboardingTourShown } = useTourContext(); - - const { openLeftPanel, openRightPanel } = useExpandableFlyoutApi(); - const { eventId, indexName, scopeId, isPreview, getFieldsData } = useDocumentDetailsContext(); - - const eventKind = getField(getFieldsData('event.kind')); - const isAlert = eventKind === EventKind.signal; - const isTimelineFlyoutOpen = useWhichFlyout() === Flyouts.timeline; - const showTour = - isAlert && - !isPreview && - !isTimelineFlyoutOpen && - !isGuidedOnboardingTourShown(SecurityStepId.alertsCases) && - !casesFlyoutExpanded; - - const goToLeftPanel = useCallback(() => { - openLeftPanel({ - id: DocumentDetailsLeftPanelKey, - params: { - id: eventId, - indexName, - scopeId, - }, - }); - }, [eventId, indexName, scopeId, openLeftPanel]); - - const goToOverviewTab = useCallback(() => { - openRightPanel({ - id: DocumentDetailsRightPanelKey, - path: { tab: 'overview' }, - params: { - id: eventId, - indexName, - scopeId, - }, - }); - }, [eventId, indexName, scopeId, openRightPanel]); - - const tourStepContent = useMemo( - // we append the left tour steps here to support the scenarios where the flyout left section is already expanded when starting the tour - () => [...getRightSectionTourSteps(), ...getLeftSectionTourSteps()], - [] - ); - - return showTour ? ( - - ) : null; -}); - -RightPanelTour.displayName = 'RightPanelTour'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/content.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/content.tsx index 075921de192b8..dbc530a4dd3f6 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/content.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/content.tsx @@ -7,10 +7,10 @@ import type { FC } from 'react'; import React, { useMemo } from 'react'; -import { FlyoutBody } from '@kbn/security-solution-common'; import { FLYOUT_BODY_TEST_ID } from './test_ids'; import type { RightPanelPaths } from '.'; import type { RightPanelTabType } from './tabs'; +import { FlyoutBody } from '../../shared/components/flyout_body'; export interface PanelContentProps { /** diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/header.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/header.tsx index b7aea63ee9a24..aa1de9eda1b00 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/header.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/header.tsx @@ -9,9 +9,10 @@ import type { EuiFlyoutHeader } from '@elastic/eui'; import { EuiSpacer, EuiTab } from '@elastic/eui'; import type { FC } from 'react'; import React, { memo, useMemo } from 'react'; -import { FlyoutHeader, FlyoutHeaderTabs } from '@kbn/security-solution-common'; import type { RightPanelPaths } from '.'; import type { RightPanelTabType } from './tabs'; +import { FlyoutHeader } from '../../shared/components/flyout_header'; +import { FlyoutHeaderTabs } from '../../shared/components/flyout_header_tabs'; import { AlertHeaderTitle } from './components/alert_header_title'; import { EventHeaderTitle } from './components/event_header_title'; import { useDocumentDetailsContext } from '../shared/context'; 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 1f9006b8d04a8..56c24d9562091 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 @@ -17,7 +17,6 @@ import type { DocumentDetailsProps } from '../shared/types'; import { PanelNavigation } from './navigation'; import { PanelHeader } from './header'; import { PanelContent } from './content'; -import { RightPanelTour } from './components/tour'; import type { RightPanelTabType } from './tabs'; import { PanelFooter } from './footer'; import { useFlyoutIsExpandable } from './hooks/use_flyout_is_expandable'; @@ -76,7 +75,6 @@ export const RightPanel: FC> = memo(({ path }) => return ( <> - {flyoutIsExpandable && } - -
+ ), content: , }; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/document_details/right/test_ids.ts index 54199abf55de8..89a71e5fd17ba 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/test_ids.ts @@ -12,6 +12,5 @@ export const FLYOUT_FOOTER_TEST_ID = `${PREFIX}Footer` as const; export const FLYOUT_FOOTER_DEOPDOEN_BUTTON_TEST_ID = `${FLYOUT_FOOTER_TEST_ID}DropdownButton` as const; export const OVERVIEW_TAB_TEST_ID = `${PREFIX}OverviewTab` as const; -export const OVERVIEW_TAB_LABEL_TEST_ID = `${PREFIX}OverviewTabLabel` as const; export const TABLE_TAB_TEST_ID = `${PREFIX}TableTab` as const; export const JSON_TAB_TEST_ID = `${PREFIX}JsonTab` as const; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/shared/components/flyout_tour.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/shared/components/flyout_tour.test.tsx deleted file mode 100644 index 246bd6a8940c9..0000000000000 --- a/x-pack/plugins/security_solution/public/flyout/document_details/shared/components/flyout_tour.test.tsx +++ /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 React from 'react'; -import { type FlyoutTourProps, FlyoutTour } from './flyout_tour'; -import { render, waitFor, fireEvent } from '@testing-library/react'; -import { - createMockStore, - createSecuritySolutionStorageMock, - TestProviders, -} from '../../../../common/mock'; -import { useKibana as mockUseKibana } from '../../../../common/lib/kibana/__mocks__'; -import { useKibana } from '../../../../common/lib/kibana'; -import { FLYOUT_TOUR_TEST_ID } from './test_ids'; - -jest.mock('../../../../common/lib/kibana'); - -const mockedUseKibana = mockUseKibana(); - -const { storage: storageMock } = createSecuritySolutionStorageMock(); -const mockStore = createMockStore(undefined, undefined, undefined, storageMock); - -const content = [1, 2, 3, 4].map((i) => ({ - title: `step${i}`, - content:

{`step${i}`}

, - stepNumber: i, - anchor: `step${i}`, -})); - -const tourProps = { - tourStepContent: content, - totalSteps: 4, -}; -const goToLeftPanel = jest.fn(); -const goToOverviewTab = jest.fn(); - -const renderTour = (props: FlyoutTourProps) => - render( - - -
-
-
-
- - ); - -describe('Flyout Tour', () => { - beforeEach(() => { - (useKibana as jest.Mock).mockReturnValue({ - ...mockedUseKibana, - services: { - ...mockedUseKibana.services, - storage: storageMock, - }, - }); - - storageMock.clear(); - }); - - it('should render tour steps', async () => { - const wrapper = renderTour(tourProps); - - await waitFor(() => { - expect(wrapper.getByTestId(`${FLYOUT_TOUR_TEST_ID}-1`)).toBeVisible(); - }); - fireEvent.click(wrapper.getByText('Next')); - await waitFor(() => { - expect(wrapper.getByTestId(`${FLYOUT_TOUR_TEST_ID}-2`)).toBeVisible(); - }); - fireEvent.click(wrapper.getByText('Next')); - await waitFor(() => { - expect(wrapper.getByTestId(`${FLYOUT_TOUR_TEST_ID}-3`)).toBeVisible(); - }); - fireEvent.click(wrapper.getByText('Next')); - await waitFor(() => { - expect(wrapper.getByTestId(`${FLYOUT_TOUR_TEST_ID}-4`)).toBeVisible(); - }); - await waitFor(() => { - expect(wrapper.getByText('Finish')).toBeVisible(); - }); - }); - - it('should call goToOverview at step 1', () => { - renderTour({ - ...tourProps, - goToOverviewTab, - }); - expect(goToOverviewTab).toHaveBeenCalled(); - }); - - it('should call goToLeftPanel when after step 3', async () => { - const wrapper = renderTour({ - ...tourProps, - goToLeftPanel, - }); - - await waitFor(() => { - expect(wrapper.getByTestId(`${FLYOUT_TOUR_TEST_ID}-1`)).toBeVisible(); - }); - fireEvent.click(wrapper.getByText('Next')); - await waitFor(() => { - expect(wrapper.getByTestId(`${FLYOUT_TOUR_TEST_ID}-2`)).toBeVisible(); - }); - fireEvent.click(wrapper.getByText('Next')); - await waitFor(() => { - expect(wrapper.getByTestId(`${FLYOUT_TOUR_TEST_ID}-3`)).toBeVisible(); - }); - fireEvent.click(wrapper.getByText('Next')); - - expect(goToLeftPanel).toHaveBeenCalled(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/shared/components/flyout_tour.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/shared/components/flyout_tour.tsx deleted file mode 100644 index c5ee76ca558a9..0000000000000 --- a/x-pack/plugins/security_solution/public/flyout/document_details/shared/components/flyout_tour.tsx +++ /dev/null @@ -1,160 +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. - */ - -/* - * This timeline tour only valid for 8.12 release is not needed for 8.13 - * - * */ - -import type { FC } from 'react'; -import React, { useCallback, useState, useEffect } from 'react'; -import { EuiButton, EuiButtonEmpty, EuiTourStep } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { tourConfig, type TourState } from '../utils/tour_step_config'; -import type { FlyoutTourStepsProps } from '../utils/tour_step_config'; -import { NEW_FEATURES_TOUR_STORAGE_KEYS } from '../../../../../common/constants'; -import { useKibana } from '../../../../common/lib/kibana'; -import { FLYOUT_TOUR_TEST_ID } from './test_ids'; - -export interface FlyoutTourProps { - /** - * Content to be displayed in each tour card - */ - tourStepContent: FlyoutTourStepsProps[]; - /** - * Total number of tour steps - */ - totalSteps: number; - /** - * Callback to go to overview tab before tour - */ - goToOverviewTab?: () => void; - /** - * Callback to go to open left panel - */ - goToLeftPanel?: () => void; -} - -const MAX_POPOVER_WIDTH = 500; -const TOUR_SUBTITLE = i18n.translate('xpack.securitySolution.flyout.tour.subtitle', { - defaultMessage: 'A redesigned alert experience', -}); - -/** - * Shared component that generates tour steps based on supplied tour step content. - * Supports tours being shown in different panels and manages state via local storage - */ -export const FlyoutTour: FC = ({ - tourStepContent, - totalSteps, - goToOverviewTab, - goToLeftPanel, -}) => { - const { - services: { storage }, - } = useKibana(); - - const [tourState, setTourState] = useState(() => { - const restoredTourState = storage.get(NEW_FEATURES_TOUR_STORAGE_KEYS.FLYOUT); - if (restoredTourState != null) { - return restoredTourState; - } - return tourConfig; - }); - - useEffect(() => { - storage.set(NEW_FEATURES_TOUR_STORAGE_KEYS.FLYOUT, tourState); - if (tourState.isTourActive && tourState.currentTourStep === 1 && goToOverviewTab) { - goToOverviewTab(); - } - }, [storage, tourState, goToOverviewTab]); - - const nextStep = useCallback(() => { - setTourState((prev) => { - if (prev.currentTourStep === 3 && goToLeftPanel) { - goToLeftPanel(); - } - return { - ...prev, - currentTourStep: prev.currentTourStep + 1, - }; - }); - }, [goToLeftPanel]); - - const finishTour = useCallback(() => { - setTourState((prev) => { - return { - ...prev, - isTourActive: false, - }; - }); - }, []); - - const getFooterAction = useCallback( - (step: number) => { - // if it's the last step, we don't want to show the next button - return step === totalSteps ? ( - - {i18n.translate('xpack.securitySolution.flyout.tour.finish.text', { - defaultMessage: 'Finish', - })} - - ) : ( - [ - - {i18n.translate('xpack.securitySolution.flyout.tour.exit.text', { - defaultMessage: 'Exit', - })} - , - - {i18n.translate('xpack.securitySolution.flyout.tour.Next.text', { - defaultMessage: 'Next', - })} - , - ] - ); - }, - [finishTour, nextStep, totalSteps] - ); - - // Do not show tour if it is inactive - if (!tourState.isTourActive) { - return null; - } - - return ( - <> - {tourStepContent.map((steps) => { - const stepCount = steps.stepNumber; - if (tourState.currentTourStep !== stepCount) return null; - const panelProps = { - 'data-test-subj': `${FLYOUT_TOUR_TEST_ID}-${stepCount}`, - }; - - return ( - - ); - })} - - ); -}; - -FlyoutTour.displayName = 'FlyoutTour'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/shared/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/document_details/shared/components/test_ids.ts index 7c2ce2ff5870b..b250e9a688380 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/shared/components/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/document_details/shared/components/test_ids.ts @@ -7,7 +7,6 @@ import { PREFIX } from '../../../shared/test_ids'; -export const FLYOUT_TOUR_TEST_ID = `${PREFIX}Tour` as const; export const FLYOUT_PREVIEW_LINK_TEST_ID = `${PREFIX}PreviewLink` as const; export const SESSION_VIEW_UPSELL_TEST_ID = `${PREFIX}SessionViewUpsell` as const; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/shared/context.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/shared/context.tsx index f2c5d8bfa530f..12e2ad4f2a0b6 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/shared/context.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/shared/context.tsx @@ -9,8 +9,9 @@ import type { BrowserFields, TimelineEventsDetailsItem } from '@kbn/timelines-pl 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 { FlyoutError, FlyoutLoading } from '@kbn/security-solution-common/src/flyout'; import { useEventDetails } from './hooks/use_event_details'; +import { FlyoutError } from '../../shared/components/flyout_error'; +import { FlyoutLoading } from '../../shared/components/flyout_loading'; import type { SearchHit } from '../../../../common/search_strategy'; import { useBasicDataFromDetailsData } from './hooks/use_basic_data_from_details_data'; import type { DocumentDetailsProps } from './types'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/shared/utils/tour_step_config.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/shared/utils/tour_step_config.tsx deleted file mode 100644 index 3f597e47c770c..0000000000000 --- a/x-pack/plugins/security_solution/public/flyout/document_details/shared/utils/tour_step_config.tsx +++ /dev/null @@ -1,199 +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 { EuiText, EuiCode, type EuiTourStepProps } from '@elastic/eui'; -import React from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { i18n } from '@kbn/i18n'; -import { HEADER_NAVIGATION_BUTTON_TEST_ID } from '@kbn/security-solution-common'; -import { OVERVIEW_TAB_LABEL_TEST_ID } from '../../right/test_ids'; -import { RULE_SUMMARY_BUTTON_TEST_ID } from '../../right/components/test_ids'; -import { - INSIGHTS_TAB_PREVALENCE_BUTTON_LABEL_TEST_ID, - INSIGHTS_TAB_ENTITIES_BUTTON_LABEL_TEST_ID, -} from '../../left/tabs/test_ids'; - -export const FLYOUT_TOUR_CONFIG_ANCHORS = { - OVERVIEW_TAB: OVERVIEW_TAB_LABEL_TEST_ID, - RULE_PREVIEW: RULE_SUMMARY_BUTTON_TEST_ID, - NAVIGATION_BUTTON: HEADER_NAVIGATION_BUTTON_TEST_ID, - ENTITIES: INSIGHTS_TAB_ENTITIES_BUTTON_LABEL_TEST_ID, - PREVALENCE: INSIGHTS_TAB_PREVALENCE_BUTTON_LABEL_TEST_ID, -}; - -export interface TourState { - /** - * The current step number - */ - currentTourStep: number; - /** - * True if tour is active (user has not completed or exited the tour) - */ - isTourActive: boolean; -} - -export const tourConfig: TourState = { - currentTourStep: 1, - isTourActive: true, -}; - -export interface FlyoutTourStepsProps { - /** - * Title of the tour step - */ - title: string; - /** - * Content of tour step - */ - content: JSX.Element; - /** - * Step number - */ - stepNumber: number; - /** - * Data test subject of the anchor component - */ - anchor: string; - /** - * Optional anchor position prop - */ - anchorPosition?: EuiTourStepProps['anchorPosition']; -} - -export const getRightSectionTourSteps = (): FlyoutTourStepsProps[] => { - const rightSectionTourSteps: FlyoutTourStepsProps[] = [ - { - title: i18n.translate('xpack.securitySolution.flyout.tour.overview.title', { - defaultMessage: 'More ways to understand your alerts', - }), - content: ( - - - {i18n.translate('xpack.securitySolution.flyout.tour.overview.entities.text', { - defaultMessage: 'Entities', - })} - - ), - prevalence: ( - - {i18n.translate('xpack.securitySolution.flyout.tour.overview.prevalence.text', { - defaultMessage: 'Prevalence', - })} - - ), - }} - /> - - ), - stepNumber: 1, - anchor: FLYOUT_TOUR_CONFIG_ANCHORS.OVERVIEW_TAB, - anchorPosition: 'downCenter', - }, - { - title: i18n.translate('xpack.securitySolution.flyout.tour.preview.title', { - defaultMessage: 'A quick way to access rule details', - }), - content: ( - - - {i18n.translate('xpack.securitySolution.flyout.tour.rulePreview.text', { - defaultMessage: 'Show rule summary', - })} - - ), - }} - /> - - ), - stepNumber: 2, - anchor: FLYOUT_TOUR_CONFIG_ANCHORS.RULE_PREVIEW, - anchorPosition: 'rightUp', - }, - { - title: i18n.translate('xpack.securitySolution.flyout.tour.expandDetails.title', { - defaultMessage: 'An expanded view of important alert details', - }), - content: ( - - - - ), - stepNumber: 3, - anchor: FLYOUT_TOUR_CONFIG_ANCHORS.NAVIGATION_BUTTON, - anchorPosition: 'downCenter', - }, - ]; - return rightSectionTourSteps; -}; - -export const getLeftSectionTourSteps = (): FlyoutTourStepsProps[] => { - return [ - { - title: i18n.translate('xpack.securitySolution.flyout.tour.entities.title', { - defaultMessage: 'New host and user insights are available', - }), - content: ( - - - {i18n.translate('xpack.securitySolution.flyout.tour.entities.text', { - defaultMessage: 'Entities', - })} - - ), - }} - /> - - ), - stepNumber: 4, - anchor: FLYOUT_TOUR_CONFIG_ANCHORS.ENTITIES, - anchorPosition: 'rightUp', - }, - { - title: i18n.translate('xpack.securitySolution.flyout.tour.prevalence.title', { - defaultMessage: 'New host and user insights are available', - }), - content: ( - - - {i18n.translate('xpack.securitySolution.flyout.tour.prevalence.text', { - defaultMessage: 'Prevalence', - })} - - ), - }} - /> - - ), - stepNumber: 5, - anchor: FLYOUT_TOUR_CONFIG_ANCHORS.PREVALENCE, - anchorPosition: 'rightUp', - }, - ]; -}; diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/host_preview/footer.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/host_preview/footer.tsx index 3ea6d7a5e0438..e550da28b532a 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/host_preview/footer.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/host_preview/footer.tsx @@ -9,7 +9,7 @@ import { EuiLink, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React, { useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import { FlyoutFooter } from '@kbn/security-solution-common'; +import { FlyoutFooter } from '../../shared/components/flyout_footer'; import { HostPanelKey } from '../host_right'; export interface HostPreviewPanelFooterProps { diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/content.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/content.tsx index 4538f53f0bd81..2e7c14fc38027 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/content.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/content.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { EuiHorizontalRule } from '@elastic/eui'; -import { FlyoutBody } from '@kbn/security-solution-common'; +import { FlyoutBody } from '../../shared/components/flyout_body'; import { EntityInsight } from '../../../cloud_security_posture/components/entity_insight'; import { AssetCriticalityAccordion } from '../../../entity_analytics/components/asset_criticality/asset_criticality_selector'; import { FlyoutRiskSummary } from '../../../entity_analytics/components/risk_summary_flyout/risk_summary'; diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/header.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/header.tsx index 706790770eb6c..b5df2f81d1b0a 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/header.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/header.tsx @@ -9,11 +9,12 @@ import { EuiSpacer, EuiBadge, EuiText, EuiFlexItem, EuiFlexGroup } from '@elasti import { FormattedMessage } from '@kbn/i18n-react'; import React, { useMemo } from 'react'; import { SecurityPageName } from '@kbn/security-solution-navigation'; -import { FlyoutHeader, FlyoutTitle } from '@kbn/security-solution-common'; import type { HostItem } from '../../../../common/search_strategy'; import { getHostDetailsUrl } from '../../../common/components/link_to'; import { SecuritySolutionLinkAnchor } from '../../../common/components/links'; import { PreferenceFormattedDate } from '../../../common/components/formatted_date'; +import { FlyoutHeader } from '../../shared/components/flyout_header'; +import { FlyoutTitle } from '../../shared/components/flyout_title'; import type { ObservedEntityData } from '../shared/components/observed_entity/types'; interface HostPanelHeaderProps { diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx index 9c75287ed0657..adc54b58f75cb 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx @@ -9,7 +9,6 @@ import React, { useCallback, useMemo } from 'react'; import type { FlyoutPanelProps } from '@kbn/expandable-flyout'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import { FlyoutLoading, FlyoutNavigation } from '@kbn/security-solution-common'; import { buildEntityFlyoutPreviewQuery } from '@kbn/cloud-security-posture-common'; import { useMisconfigurationPreview } from '@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview'; import { useVulnerabilitiesPreview } from '@kbn/cloud-security-posture/src/hooks/use_vulnerabilities_preview'; @@ -26,6 +25,8 @@ import { useGlobalTime } from '../../../common/containers/use_global_time'; import type { HostItem } from '../../../../common/search_strategy'; import { buildHostNamesFilter } from '../../../../common/search_strategy'; import { RiskScoreEntity } from '../../../../common/entity_analytics/risk_engine'; +import { FlyoutLoading } from '../../shared/components/flyout_loading'; +import { FlyoutNavigation } from '../../shared/components/flyout_navigation'; import { HostPanelContent } from './content'; import { HostPanelHeader } from './header'; import { AnomalyTableProvider } from '../../../common/components/ml/anomaly/anomaly_table_provider'; diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/left_panel/left_panel_content.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/left_panel/left_panel_content.tsx index f52480285a567..5a66a5b305611 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/left_panel/left_panel_content.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/left_panel/left_panel_content.tsx @@ -9,7 +9,7 @@ import { useEuiBackgroundColor } from '@elastic/eui'; import type { VFC } from 'react'; import React, { useMemo } from 'react'; import { css } from '@emotion/react'; -import { FlyoutBody } from '@kbn/security-solution-common'; +import { FlyoutBody } from '../../../../shared/components/flyout_body'; import type { EntityDetailsLeftPanelTab, LeftPanelTabsType } from './left_panel_header'; export interface PanelContentProps { diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/left_panel/left_panel_header.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/left_panel/left_panel_header.tsx index 438f75e7a4ccb..08623c941ba67 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/left_panel/left_panel_header.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/left_panel/left_panel_header.tsx @@ -9,7 +9,7 @@ import { EuiTab, EuiTabs, useEuiBackgroundColor } from '@elastic/eui'; import type { ReactElement, VFC } from 'react'; import React, { memo } from 'react'; import { css } from '@emotion/react'; -import { FlyoutHeader } from '@kbn/security-solution-common'; +import { FlyoutHeader } from '../../../../shared/components/flyout_header'; export type LeftPanelTabsType = Array<{ id: EntityDetailsLeftPanelTab; diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.tsx index ae3e99cc17cfe..8e6cf3a9ee9d2 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.tsx @@ -8,9 +8,9 @@ import React, { useMemo } from 'react'; import type { FlyoutPanelProps, PanelPath } from '@kbn/expandable-flyout'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import { FlyoutLoading } from '@kbn/security-solution-common'; import { useManagedUser } from '../shared/hooks/use_managed_user'; import { useTabs } from './tabs'; +import { FlyoutLoading } from '../../shared/components/flyout_loading'; import type { EntityDetailsLeftPanelTab, LeftPanelTabsType, diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs/asset_document.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs/asset_document.tsx index 31053cf88d931..3aa4a72ffe898 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs/asset_document.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs/asset_document.tsx @@ -12,10 +12,10 @@ import { FormattedMessage } from '@kbn/i18n-react'; import type { EuiButtonGroupOptionProps } from '@elastic/eui'; import { EuiButtonGroup } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FlyoutBody } from '@kbn/security-solution-common'; import { JsonTab } from '../../../document_details/right/tabs/json_tab'; import { TableTab } from '../../../document_details/right/tabs/table_tab'; import { FLYOUT_BODY_TEST_ID, JSON_TAB_TEST_ID, TABLE_TAB_TEST_ID } from './test_ids'; +import { FlyoutBody } from '../../../shared/components/flyout_body'; export type RightPanelPaths = 'overview' | 'table' | 'json'; export interface AssetDocumentPanelProps extends FlyoutPanelProps { diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_preview/footer.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_preview/footer.tsx index 6921d1a73d4e2..7f55feb7a347c 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_preview/footer.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_preview/footer.tsx @@ -9,7 +9,7 @@ import { EuiLink, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React, { useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import { FlyoutFooter } from '@kbn/security-solution-common'; +import { FlyoutFooter } from '../../shared/components/flyout_footer'; import { UserPanelKey } from '../user_right'; export interface UserPreviewPanelFooterProps { diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/components/managed_user_accordion.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/components/managed_user_accordion.tsx index 84f6c96cb772b..8d9007713549e 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/components/managed_user_accordion.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/components/managed_user_accordion.tsx @@ -11,14 +11,13 @@ import React from 'react'; import { css } from '@emotion/react'; import { FormattedMessage } from '@kbn/i18n-react'; import { get } from 'lodash/fp'; -import { ExpandablePanel } from '@kbn/security-solution-common'; import { EntityDetailsLeftPanelTab } from '../../shared/components/left_panel/left_panel_header'; +import { ExpandablePanel } from '../../../shared/components/expandable_panel'; import type { ManagedUserFields } from '../../../../../common/search_strategy/security_solution/users/managed_details'; import { FormattedRelativePreferenceDate } from '../../../../common/components/formatted_date'; import { ONE_WEEK_IN_HOURS } from '../../shared/constants'; import { UserAssetTableType } from '../../../../explore/users/store/model'; - interface ManagedUserAccordionProps { children: React.ReactNode; title: string; diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/content.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/content.tsx index 8bcf5dd690200..0dbc1faa5cb42 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/content.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/content.tsx @@ -8,7 +8,6 @@ import { EuiHorizontalRule } from '@elastic/eui'; import React from 'react'; -import { FlyoutBody } from '@kbn/security-solution-common'; import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; import { AssetCriticalityAccordion } from '../../../entity_analytics/components/asset_criticality/asset_criticality_selector'; @@ -19,6 +18,7 @@ import { ManagedUser } from './components/managed_user'; import type { ManagedUserData } from './types'; import type { RiskScoreEntity, UserItem } from '../../../../common/search_strategy'; import { USER_PANEL_RISK_SCORE_QUERY_ID } from '.'; +import { FlyoutBody } from '../../shared/components/flyout_body'; import { ObservedEntity } from '../shared/components/observed_entity'; import type { ObservedEntityData } from '../shared/components/observed_entity/types'; import { useObservedUserItems } from './hooks/use_observed_user_items'; diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/header.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/header.tsx index 8fb54478b3c4f..e141779b559cf 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/header.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/header.tsx @@ -10,7 +10,6 @@ import { FormattedMessage } from '@kbn/i18n-react'; import React, { useMemo } from 'react'; import { max } from 'lodash/fp'; import { SecurityPageName } from '@kbn/security-solution-navigation'; -import { FlyoutHeader, FlyoutTitle } from '@kbn/security-solution-common'; import type { UserItem } from '../../../../common/search_strategy'; import { ManagedUserDatasetKey } from '../../../../common/search_strategy/security_solution/users/managed_details'; import { getUsersDetailsUrl } from '../../../common/components/link_to/redirect_to_users'; @@ -18,6 +17,8 @@ import type { ManagedUserData } from './types'; import { SecuritySolutionLinkAnchor } from '../../../common/components/links'; import { PreferenceFormattedDate } from '../../../common/components/formatted_date'; +import { FlyoutHeader } from '../../shared/components/flyout_header'; +import { FlyoutTitle } from '../../shared/components/flyout_title'; import type { ObservedEntityData } from '../shared/components/observed_entity/types'; interface UserPanelHeaderProps { diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx index ec55fb292abfd..3a60c06e3faea 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx @@ -8,9 +8,8 @@ import React, { useCallback, useMemo } from 'react'; import type { FlyoutPanelProps } from '@kbn/expandable-flyout'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import { FlyoutLoading, FlyoutNavigation } from '@kbn/security-solution-common/src/flyout'; -import { buildEntityFlyoutPreviewQuery } from '@kbn/cloud-security-posture-common'; import { useMisconfigurationPreview } from '@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview'; +import { buildEntityFlyoutPreviewQuery } from '@kbn/cloud-security-posture-common'; import { useRefetchQueryById } from '../../../entity_analytics/api/hooks/use_refetch_query_by_id'; import type { Refetch } from '../../../common/types'; import { RISK_INPUTS_TAB_QUERY_ID } from '../../../entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs_tab'; @@ -26,6 +25,8 @@ import { useGlobalTime } from '../../../common/containers/use_global_time'; import { AnomalyTableProvider } from '../../../common/components/ml/anomaly/anomaly_table_provider'; import { buildUserNamesFilter } from '../../../../common/search_strategy'; import { RiskScoreEntity } from '../../../../common/entity_analytics/risk_engine'; +import { FlyoutLoading } from '../../shared/components/flyout_loading'; +import { FlyoutNavigation } from '../../shared/components/flyout_navigation'; import { UserPanelContent } from './content'; import { UserPanelHeader } from './header'; import { UserDetailsPanelKey } from '../user_details_left'; diff --git a/x-pack/plugins/security_solution/public/flyout/network_details/content.tsx b/x-pack/plugins/security_solution/public/flyout/network_details/content.tsx index 3634f73ef5a7f..8c9aa355ed43a 100644 --- a/x-pack/plugins/security_solution/public/flyout/network_details/content.tsx +++ b/x-pack/plugins/security_solution/public/flyout/network_details/content.tsx @@ -8,8 +8,8 @@ import type { FC } from 'react'; import React, { memo } from 'react'; import { EuiSpacer } from '@elastic/eui'; -import { FlyoutBody } from '@kbn/security-solution-common'; import { NetworkDetails } from './components/network_details'; +import { FlyoutBody } from '../shared/components/flyout_body'; import type { FlowTargetSourceDest } from '../../../common/search_strategy'; export interface PanelContentProps { diff --git a/x-pack/plugins/security_solution/public/flyout/network_details/header.tsx b/x-pack/plugins/security_solution/public/flyout/network_details/header.tsx index 1ef47c00689d9..8ffceb345b1e0 100644 --- a/x-pack/plugins/security_solution/public/flyout/network_details/header.tsx +++ b/x-pack/plugins/security_solution/public/flyout/network_details/header.tsx @@ -9,10 +9,11 @@ import type { FC } from 'react'; import React, { memo, useMemo } from 'react'; import type { EuiFlyoutHeader } from '@elastic/eui'; import { SecurityPageName } from '@kbn/deeplinks-security'; -import { FlyoutHeader, FlyoutTitle } from '@kbn/security-solution-common'; import { getNetworkDetailsUrl } from '../../common/components/link_to'; import { SecuritySolutionLinkAnchor } from '../../common/components/links'; import type { FlowTargetSourceDest } from '../../../common/search_strategy'; +import { FlyoutHeader } from '../shared/components/flyout_header'; +import { FlyoutTitle } from '../shared/components/flyout_title'; import { encodeIpv6 } from '../../common/lib/helpers'; export interface PanelHeaderProps extends React.ComponentProps { diff --git a/x-pack/plugins/security_solution/public/flyout/rule_details/preview/footer.tsx b/x-pack/plugins/security_solution/public/flyout/rule_details/preview/footer.tsx index 42c8c1a6d85b9..4440555e5fc53 100644 --- a/x-pack/plugins/security_solution/public/flyout/rule_details/preview/footer.tsx +++ b/x-pack/plugins/security_solution/public/flyout/rule_details/preview/footer.tsx @@ -8,9 +8,9 @@ import React, { memo } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FlyoutFooter } from '@kbn/security-solution-common'; import { RULE_PREVIEW_FOOTER_TEST_ID, RULE_PREVIEW_OPEN_RULE_FLYOUT_TEST_ID } from './test_ids'; import { useRuleDetailsLink } from '../../document_details/shared/hooks/use_rule_details_link'; +import { FlyoutFooter } from '../../shared/components/flyout_footer'; /** * Footer in rule preview panel diff --git a/x-pack/plugins/security_solution/public/flyout/rule_details/right/content.tsx b/x-pack/plugins/security_solution/public/flyout/rule_details/right/content.tsx index 2778fc9c7ca22..8acc6cfe9b715 100644 --- a/x-pack/plugins/security_solution/public/flyout/rule_details/right/content.tsx +++ b/x-pack/plugins/security_solution/public/flyout/rule_details/right/content.tsx @@ -8,7 +8,6 @@ import React, { memo } from 'react'; import { EuiText, EuiHorizontalRule, EuiSpacer, EuiPanel } from '@elastic/eui'; import { css } from '@emotion/css'; import { FormattedMessage } from '@kbn/i18n-react'; -import { FlyoutBody } from '@kbn/security-solution-common'; import { ExpandableSection } from '../../document_details/right/components/expandable_section'; import { RuleAboutSection } from '../../../detection_engine/rule_management/components/rule_details/rule_about_section'; import { RuleScheduleSection } from '../../../detection_engine/rule_management/components/rule_details/rule_schedule_section'; @@ -23,6 +22,7 @@ import { ACTIONS_TEST_ID, } from './test_ids'; import type { RuleResponse } from '../../../../common/api/detection_engine'; +import { FlyoutBody } from '../../shared/components/flyout_body'; const panelViewStyle = css` dt { diff --git a/x-pack/plugins/security_solution/public/flyout/rule_details/right/header.tsx b/x-pack/plugins/security_solution/public/flyout/rule_details/right/header.tsx index 3dbbcc6b5b259..294870d6eebb7 100644 --- a/x-pack/plugins/security_solution/public/flyout/rule_details/right/header.tsx +++ b/x-pack/plugins/security_solution/public/flyout/rule_details/right/header.tsx @@ -15,7 +15,6 @@ import { EuiBadge, EuiLink, } from '@elastic/eui'; -import { FlyoutHeader, FlyoutTitle } from '@kbn/security-solution-common'; import { DELETED_RULE } from '../../../detection_engine/rule_details_ui/pages/rule_details/translations'; import { CreatedBy, UpdatedBy } from '../../../detections/components/rules/rule_info'; import { @@ -27,6 +26,8 @@ import { } from './test_ids'; import type { RuleResponse } from '../../../../common/api/detection_engine'; import { useRuleDetailsLink } from '../../document_details/shared/hooks/use_rule_details_link'; +import { FlyoutHeader } from '../../shared/components/flyout_header'; +import { FlyoutTitle } from '../../shared/components/flyout_title'; export interface PanelHeaderProps { /** diff --git a/x-pack/plugins/security_solution/public/flyout/rule_details/right/index.tsx b/x-pack/plugins/security_solution/public/flyout/rule_details/right/index.tsx index 958a2d4265186..10b22e22a575c 100644 --- a/x-pack/plugins/security_solution/public/flyout/rule_details/right/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/rule_details/right/index.tsx @@ -7,13 +7,15 @@ import React, { memo } from 'react'; import type { FlyoutPanelProps } from '@kbn/expandable-flyout'; -import { FlyoutLoading, FlyoutError, FlyoutNavigation } from '@kbn/security-solution-common'; import { i18n } from '@kbn/i18n'; import { PanelContent } from './content'; import { PanelHeader } from './header'; import { PreviewFooter } from '../preview/footer'; import { useRuleDetails } from '../hooks/use_rule_details'; import { LOADING_TEST_ID } from './test_ids'; +import { FlyoutLoading } from '../../shared/components/flyout_loading'; +import { FlyoutNavigation } from '../../shared/components/flyout_navigation'; +import { FlyoutError } from '../../shared/components/flyout_error'; export interface RulePanelExpandableFlyoutProps extends FlyoutPanelProps { key: 'rule-panel' | 'rule-preview-panel'; diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/expandable_panel.stories.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/expandable_panel.stories.tsx similarity index 100% rename from x-pack/packages/security-solution/common/src/flyout/common/components/expandable_panel.stories.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/expandable_panel.stories.tsx diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/expandable_panel.test.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/expandable_panel.test.tsx similarity index 99% rename from x-pack/packages/security-solution/common/src/flyout/common/components/expandable_panel.test.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/expandable_panel.test.tsx index cc282eb1156b5..87592b608613f 100644 --- a/x-pack/packages/security-solution/common/src/flyout/common/components/expandable_panel.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/shared/components/expandable_panel.test.tsx @@ -13,7 +13,7 @@ import { EXPANDABLE_PANEL_CONTENT_TEST_ID, EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID, EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID, -} from '../test_ids'; +} from './test_ids'; import { ThemeProvider } from '@emotion/react'; import { ExpandablePanel } from './expandable_panel'; diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/expandable_panel.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/expandable_panel.tsx similarity index 97% rename from x-pack/packages/security-solution/common/src/flyout/common/components/expandable_panel.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/expandable_panel.tsx index 383bbbb341c8e..88318ee2f2cfc 100644 --- a/x-pack/packages/security-solution/common/src/flyout/common/components/expandable_panel.tsx +++ b/x-pack/plugins/security_solution/public/flyout/shared/components/expandable_panel.tsx @@ -110,7 +110,7 @@ export const ExpandablePanel: FC> = > = diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_body.test.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_body.test.tsx similarity index 100% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_body.test.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_body.test.tsx diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_body.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_body.tsx similarity index 100% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_body.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_body.tsx diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_error.stories.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_error.stories.tsx similarity index 100% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_error.stories.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_error.stories.tsx diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_error.test.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_error.test.tsx similarity index 94% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_error.test.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_error.test.tsx index f0565fe1df43f..e58d586a063b5 100644 --- a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_error.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_error.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { render } from '@testing-library/react'; import { FlyoutError } from './flyout_error'; -import { FLYOUT_ERROR_TEST_ID } from '../test_ids'; +import { FLYOUT_ERROR_TEST_ID } from './test_ids'; describe('', () => { it('should render error title and body', () => { diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_error.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_error.tsx similarity index 84% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_error.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_error.tsx index f319d80dafe12..9ebef345540fe 100644 --- a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_error.tsx +++ b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_error.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { EuiEmptyPrompt, EuiFlexItem } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { FLYOUT_ERROR_TEST_ID } from '../test_ids'; +import { FLYOUT_ERROR_TEST_ID } from './test_ids'; /** * Use this when you need to show an error state in the flyout @@ -21,7 +21,7 @@ export const FlyoutError: React.VFC = () => ( title={

@@ -30,7 +30,7 @@ export const FlyoutError: React.VFC = () => ( body={

diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_footer.test.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_footer.test.tsx similarity index 100% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_footer.test.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_footer.test.tsx diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_footer.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_footer.tsx similarity index 100% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_footer.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_footer.tsx diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_header.test.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_header.test.tsx similarity index 100% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_header.test.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_header.test.tsx diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_header.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_header.tsx similarity index 100% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_header.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_header.tsx diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_header_tabs.test.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_header_tabs.test.tsx similarity index 100% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_header_tabs.test.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_header_tabs.test.tsx diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_header_tabs.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_header_tabs.tsx similarity index 100% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_header_tabs.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_header_tabs.tsx diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_loading.stories.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_loading.stories.tsx similarity index 100% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_loading.stories.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_loading.stories.tsx diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_loading.test.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_loading.test.tsx similarity index 94% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_loading.test.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_loading.test.tsx index d55e85b3e978b..a164db8a6ce01 100644 --- a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_loading.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_loading.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { render } from '@testing-library/react'; -import { FLYOUT_LOADING_TEST_ID } from '../test_ids'; +import { FLYOUT_LOADING_TEST_ID } from './test_ids'; import { FlyoutLoading } from './flyout_loading'; describe('', () => { diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_loading.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_loading.tsx similarity index 94% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_loading.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_loading.tsx index cffe17c8ccbf1..0c98957dd929b 100644 --- a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_loading.tsx +++ b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_loading.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; import { css } from '@emotion/react'; -import { FLYOUT_LOADING_TEST_ID } from '../test_ids'; +import { FLYOUT_LOADING_TEST_ID } from './test_ids'; export interface FlyoutLoadingProps { /** diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_navigation.stories.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.stories.tsx similarity index 100% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_navigation.stories.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.stories.tsx diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_navigation.test.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.test.tsx similarity index 78% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_navigation.test.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.test.tsx index d010796008880..321245ccde86e 100644 --- a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_navigation.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.test.tsx @@ -8,61 +8,39 @@ import type { FC, PropsWithChildren } from 'react'; import React from 'react'; import { act, render } from '@testing-library/react'; +import { TestProviders } from '../../../common/mock'; import { FlyoutNavigation } from './flyout_navigation'; import { COLLAPSE_DETAILS_BUTTON_TEST_ID, EXPAND_DETAILS_BUTTON_TEST_ID, HEADER_ACTIONS_TEST_ID, -} from '../test_ids'; +} from './test_ids'; +import type { ExpandableFlyoutState } from '@kbn/expandable-flyout'; import { - ExpandableFlyoutApi, - ExpandableFlyoutProvider, - ExpandableFlyoutState, useExpandableFlyoutApi, + type ExpandableFlyoutApi, useExpandableFlyoutState, } from '@kbn/expandable-flyout'; -import { I18nProvider } from '@kbn/i18n-react'; const expandDetails = jest.fn(); -const mockFlyoutCloseLeftPanel = jest.fn(); + +const ExpandableFlyoutTestProviders: FC> = ({ children }) => { + return {children}; +}; jest.mock('@kbn/expandable-flyout', () => ({ - useExpandableFlyoutApi: jest.fn(() => { - return { - closeFlyout: jest.fn(), - closeLeftPanel: jest.fn(), - closePreviewPanel: jest.fn(), - closeRightPanel: jest.fn(), - previousPreviewPanel: jest.fn(), - openFlyout: jest.fn(), - openLeftPanel: jest.fn(), - openPreviewPanel: jest.fn(), - openRightPanel: jest.fn(), - }; - }), + useExpandableFlyoutApi: jest.fn(), useExpandableFlyoutState: jest.fn(), ExpandableFlyoutProvider: ({ children }: React.PropsWithChildren<{}>) => <>{children}, - withExpandableFlyoutProvider: (Component: React.ComponentType) => { - return (props: T) => { - return ; - }; - }, - ExpandableFlyout: jest.fn(), })); -const ExpandableFlyoutTestProviders: FC> = ({ children }) => { - return ( - - {children} - - ); -}; +const flyoutContextValue = { + closeLeftPanel: jest.fn(), +} as unknown as ExpandableFlyoutApi; describe('', () => { beforeEach(() => { - jest.mocked(useExpandableFlyoutApi).mockReturnValue({ - closeLeftPanel: mockFlyoutCloseLeftPanel, - } as unknown as ExpandableFlyoutApi); + jest.mocked(useExpandableFlyoutApi).mockReturnValue(flyoutContextValue); jest.mocked(useExpandableFlyoutState).mockReturnValue({} as unknown as ExpandableFlyoutState); }); @@ -97,7 +75,7 @@ describe('', () => { expect(queryByTestId(EXPAND_DETAILS_BUTTON_TEST_ID)).not.toBeInTheDocument(); getByTestId(COLLAPSE_DETAILS_BUTTON_TEST_ID).click(); - expect(mockFlyoutCloseLeftPanel).toHaveBeenCalled(); + expect(flyoutContextValue.closeLeftPanel).toHaveBeenCalled(); }); }); diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_navigation.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.tsx similarity index 88% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_navigation.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.tsx index 961629147d781..1915c5a4484a4 100644 --- a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_navigation.tsx +++ b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.tsx @@ -22,8 +22,7 @@ import { HEADER_ACTIONS_TEST_ID, COLLAPSE_DETAILS_BUTTON_TEST_ID, EXPAND_DETAILS_BUTTON_TEST_ID, - HEADER_NAVIGATION_BUTTON_TEST_ID, -} from '../test_ids'; +} from './test_ids'; export interface FlyoutNavigationProps { /** @@ -63,14 +62,14 @@ export const FlyoutNavigation: FC = memo( size="s" data-test-subj={COLLAPSE_DETAILS_BUTTON_TEST_ID} aria-label={i18n.translate( - 'securitySolutionPackages.flyout.right.header.collapseDetailButtonAriaLabel', + 'xpack.securitySolution.flyout.right.header.collapseDetailButtonAriaLabel', { defaultMessage: 'Collapse details', } )} > @@ -87,14 +86,14 @@ export const FlyoutNavigation: FC = memo( size="s" data-test-subj={EXPAND_DETAILS_BUTTON_TEST_ID} aria-label={i18n.translate( - 'securitySolutionPackages.flyout.right.header.expandDetailButtonAriaLabel', + 'xpack.securitySolution.flyout.right.header.expandDetailButtonAriaLabel', { defaultMessage: 'Expand details', } )} > @@ -116,7 +115,7 @@ export const FlyoutNavigation: FC = memo( height: ${euiTheme.size.xxl}; `} > - + {flyoutIsExpandable && expandDetails && (isExpanded ? collapseButton : expandButton)} {actions && ( diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_title.stories.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_title.stories.tsx similarity index 100% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_title.stories.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_title.stories.tsx diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_title.test.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_title.test.tsx similarity index 98% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_title.test.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_title.test.tsx index 3fde2b034219b..1f2d0c128f411 100644 --- a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_title.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_title.test.tsx @@ -12,7 +12,7 @@ import { TITLE_HEADER_ICON_TEST_ID, TITLE_HEADER_TEXT_TEST_ID, TITLE_LINK_ICON_TEST_ID, -} from '../test_ids'; +} from './test_ids'; const title = 'test title'; const TEST_ID = 'test'; diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/flyout_title.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_title.tsx similarity index 100% rename from x-pack/packages/security-solution/common/src/flyout/common/components/flyout_title.tsx rename to x-pack/plugins/security_solution/public/flyout/shared/components/flyout_title.tsx diff --git a/x-pack/packages/security-solution/common/src/flyout/common/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/shared/components/test_ids.ts similarity index 93% rename from x-pack/packages/security-solution/common/src/flyout/common/test_ids.ts rename to x-pack/plugins/security_solution/public/flyout/shared/components/test_ids.ts index 60ccb0a234fde..f8a589f31561e 100644 --- a/x-pack/packages/security-solution/common/src/flyout/common/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/shared/components/test_ids.ts @@ -5,7 +5,7 @@ * 2.0. */ -export const PREFIX = 'securitySolutionFlyout' as const; +import { PREFIX } from '../test_ids'; export const FLYOUT_ERROR_TEST_ID = `${PREFIX}Error` as const; export const FLYOUT_LOADING_TEST_ID = `${PREFIX}Loading` as const; @@ -27,7 +27,6 @@ export const EXPANDABLE_PANEL_CONTENT_TEST_ID = (dataTestSubj: string) => `${dat /* Header Navigation */ const FLYOUT_NAVIGATION_TEST_ID = `${PREFIX}Navigation` as const; -export const HEADER_NAVIGATION_BUTTON_TEST_ID = `${PREFIX}NavigationButton` as const; export const EXPAND_DETAILS_BUTTON_TEST_ID = `${FLYOUT_NAVIGATION_TEST_ID}ExpandDetailButton` as const; export const COLLAPSE_DETAILS_BUTTON_TEST_ID = diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/fleet.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/fleet.ts index e122eebedfc33..5372b46e11f9e 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/tasks/fleet.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/fleet.ts @@ -22,7 +22,7 @@ import { } from '@kbn/fleet-plugin/common'; import type { GetOneAgentResponse, - PutAgentReassignResponse, + PostAgentReassignResponse, UpdateAgentPolicyResponse, } from '@kbn/fleet-plugin/common/types'; import { uninstallTokensRouteService } from '@kbn/fleet-plugin/common/services/routes'; @@ -56,10 +56,10 @@ export const getAgentByHostName = (hostname: string): Cypress.Chainable = export const reassignAgentPolicy = ( agentId: string, agentPolicyId: string -): Cypress.Chainable> => - request({ +): Cypress.Chainable> => + request({ url: agentRouteService.getReassignPath(agentId), - method: 'PUT', + method: 'POST', body: { policy_id: agentPolicyId, }, diff --git a/x-pack/plugins/security_solution/public/management/links.ts b/x-pack/plugins/security_solution/public/management/links.ts index d033fdf491ce8..61cbc1a511c09 100644 --- a/x-pack/plugins/security_solution/public/management/links.ts +++ b/x-pack/plugins/security_solution/public/management/links.ts @@ -229,7 +229,8 @@ export const links: LinkItem = { path: NOTES_PATH, skipUrlState: true, hideTimeline: true, - experimentalKey: 'securitySolutionNotesEnabled', + hideWhenExperimentalKey: 'securitySolutionNotesDisabled', + globalSearchDisabled: true, }, ], }; diff --git a/x-pack/plugins/security_solution/public/management/pages/index.tsx b/x-pack/plugins/security_solution/public/management/pages/index.tsx index dc8314acc276c..d558b1271dc6b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/index.tsx @@ -88,8 +88,8 @@ const NotesTelemetry = () => ( ); export const ManagementContainer = memo(() => { - const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled( - 'securitySolutionNotesEnabled' + const securitySolutionNotesDisabled = useIsExperimentalFeatureEnabled( + 'securitySolutionNotesDisabled' ); const { @@ -162,7 +162,7 @@ export const ManagementContainer = memo(() => { hasPrivilege={canReadActionsLogManagement} /> - {securitySolutionNotesEnabled && ( + {!securitySolutionNotesDisabled && ( )} diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx index ffcee81a3cb8f..75e932d5385fd 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx @@ -124,7 +124,7 @@ describe('Policy Details', () => { if (path === `${AGENT_API_ROUTES.STATUS_PATTERN}`) { asyncActions = asyncActions.then(async () => sleep()); return Promise.resolve({ - results: { events: 0, total: 5, online: 3, error: 1, offline: 1 }, + results: { events: 0, active: 5, online: 3, error: 1, offline: 1 }, success: true, }); } diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx index 7f72a8d475134..13f18d071d543 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx @@ -60,7 +60,7 @@ export const PolicyDetails = React.memo(() => { const headerRightContent = ( { expect(typeof result.current.onLoad).toBe('function'); }); - it('should not dispatch action when securitySolutionNotesEnabled is false', () => { - mockedUseIsExperimentalFeatureEnabled.mockReturnValue(false); + it('should not dispatch action when securitySolutionNotesDisabled is true', () => { + mockedUseIsExperimentalFeatureEnabled.mockReturnValue(true); const { result } = renderHook(() => useFetchNotes()); result.current.onLoad([{ _id: '1' }]); @@ -54,7 +54,7 @@ describe('useFetchNotes', () => { }); it('should not dispatch action when events array is empty', () => { - mockedUseIsExperimentalFeatureEnabled.mockReturnValue(true); + mockedUseIsExperimentalFeatureEnabled.mockReturnValue(false); const { result } = renderHook(() => useFetchNotes()); result.current.onLoad([]); @@ -62,7 +62,7 @@ describe('useFetchNotes', () => { }); it('should dispatch fetchNotesByDocumentIds with correct ids when conditions are met', () => { - mockedUseIsExperimentalFeatureEnabled.mockReturnValue(true); + mockedUseIsExperimentalFeatureEnabled.mockReturnValue(false); const { result } = renderHook(() => useFetchNotes()); const events = [{ _id: '1' }, { _id: '2' }, { _id: '3' }]; @@ -74,7 +74,7 @@ describe('useFetchNotes', () => { }); it('should memoize onLoad function', () => { - mockedUseIsExperimentalFeatureEnabled.mockReturnValue(true); + mockedUseIsExperimentalFeatureEnabled.mockReturnValue(false); const { result, rerender } = renderHook(() => useFetchNotes()); const firstOnLoad = result.current.onLoad; @@ -84,7 +84,7 @@ describe('useFetchNotes', () => { expect(firstOnLoad).toBe(secondOnLoad); }); - it('should update onLoad when securitySolutionNotesEnabled changes', () => { + it('should update onLoad when securitySolutionNotesDisabled changes', () => { mockedUseIsExperimentalFeatureEnabled.mockReturnValue(true); const { result, rerender } = renderHook(() => useFetchNotes()); diff --git a/x-pack/plugins/security_solution/public/notes/hooks/use_fetch_notes.ts b/x-pack/plugins/security_solution/public/notes/hooks/use_fetch_notes.ts index 2cf599e76bcc9..cdfa8b7600dfd 100644 --- a/x-pack/plugins/security_solution/public/notes/hooks/use_fetch_notes.ts +++ b/x-pack/plugins/security_solution/public/notes/hooks/use_fetch_notes.ts @@ -22,19 +22,19 @@ export interface UseFetchNotesResult { */ export const useFetchNotes = (): UseFetchNotesResult => { const dispatch = useDispatch(); - const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled( - 'securitySolutionNotesEnabled' + const securitySolutionNotesDisabled = useIsExperimentalFeatureEnabled( + 'securitySolutionNotesDisabled' ); const onLoad = useCallback( (events: Array>) => { - if (!securitySolutionNotesEnabled || events.length === 0) return; + if (securitySolutionNotesDisabled || events.length === 0) return; const eventIds: string[] = events .map((event) => event._id) .filter((id) => id != null) as string[]; dispatch(fetchNotesByDocumentIds({ documentIds: eventIds })); }, - [dispatch, securitySolutionNotesEnabled] + [dispatch, securitySolutionNotesDisabled] ); return { onLoad }; diff --git a/x-pack/plugins/security_solution/public/notes/links.ts b/x-pack/plugins/security_solution/public/notes/links.ts index ef6c691b6246a..b09877e200fb9 100644 --- a/x-pack/plugins/security_solution/public/notes/links.ts +++ b/x-pack/plugins/security_solution/public/notes/links.ts @@ -21,5 +21,5 @@ export const links: LinkItem = { }), ], links: [], - experimentalKey: 'securitySolutionNotesEnabled', + hideWhenExperimentalKey: 'securitySolutionNotesDisabled', }; diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/alerts/alerts_card.tsx b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/alerts/alerts_card.tsx index c0369ed23d61c..85d3994d44530 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/alerts/alerts_card.tsx +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/alerts/alerts_card.tsx @@ -6,7 +6,16 @@ */ import React, { useCallback, useMemo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiLink, + EuiSpacer, + EuiText, + useEuiTheme, + COLOR_MODES_STANDARD, +} from '@elastic/eui'; import { SecurityPageName } from '@kbn/security-solution-navigation'; import { SecuritySolutionLinkButton } from '../../../../../common/components/links'; import { OnboardingCardId } from '../../../../constants'; @@ -21,6 +30,9 @@ export const AlertsCard: OnboardingCardComponent = ({ setExpandedCardId, setComplete, }) => { + const { colorMode } = useEuiTheme(); + const isDarkMode = colorMode === COLOR_MODES_STANDARD.dark; + const isIntegrationsCardComplete = useMemo( () => isCardComplete(OnboardingCardId.integrations), [isCardComplete] @@ -39,7 +51,11 @@ export const AlertsCard: OnboardingCardComponent = ({ alignItems="flexStart" > - + {i18n.ALERTS_CARD_DESCRIPTION} {!isIntegrationsCardComplete && ( diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/assistant_card.tsx b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/assistant_card.tsx index 4b87f23dd2435..b728606937020 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/assistant_card.tsx +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/assistant_card.tsx @@ -6,7 +6,16 @@ */ import React, { useCallback, useMemo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, EuiText } from '@elastic/eui'; +import { + EuiCallOut, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiLink, + EuiText, + useEuiTheme, + COLOR_MODES_STANDARD, +} from '@elastic/eui'; import { css } from '@emotion/css'; import { OnboardingCardId } from '../../../../constants'; import type { OnboardingCardComponent } from '../../../../types'; @@ -15,6 +24,7 @@ import { OnboardingCardContentPanel } from '../common/card_content_panel'; import { ConnectorCards } from './connectors/connector_cards'; import { CardCallOut } from '../common/card_callout'; import type { AssistantCardMetadata } from './types'; +import { MissingPrivilegesDescription } from './connectors/missing_privileges_tooltip'; export const AssistantCard: OnboardingCardComponent = ({ isCardComplete, @@ -22,6 +32,8 @@ export const AssistantCard: OnboardingCardComponent = ({ checkCompleteMetadata, checkComplete, }) => { + const { euiTheme, colorMode } = useEuiTheme(); + const isDarkMode = colorMode === COLOR_MODES_STANDARD.dark; const isIntegrationsCardComplete = useMemo( () => isCardComplete(OnboardingCardId.integrations), [isCardComplete] @@ -32,43 +44,60 @@ export const AssistantCard: OnboardingCardComponent = ({ }, [setExpandedCardId]); const connectors = checkCompleteMetadata?.connectors; + const canExecuteConnectors = checkCompleteMetadata?.canExecuteConnectors; + const canCreateConnectors = checkCompleteMetadata?.canCreateConnectors; return ( - - - - - {i18n.ASSISTANT_CARD_DESCRIPTION} - - - - {isIntegrationsCardComplete ? ( - - ) : ( - - - - {i18n.ASSISTANT_CARD_CALLOUT_INTEGRATIONS_BUTTON} - - - - - - } + + {canExecuteConnectors ? ( + + + + {i18n.ASSISTANT_CARD_DESCRIPTION} + + + + {isIntegrationsCardComplete ? ( + - - )} - - + ) : ( + + + + {i18n.ASSISTANT_CARD_CALLOUT_INTEGRATIONS_BUTTON} + + + + + + } + /> + + )} + + + ) : ( + + + + )} ); }; diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/assistant_check_complete.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/assistant_check_complete.ts index bdb52b3a0e614..8c0d029cee583 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/assistant_check_complete.ts +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/assistant_check_complete.ts @@ -14,8 +14,11 @@ import type { AssistantCardMetadata } from './types'; export const checkAssistantCardComplete: OnboardingCardCheckComplete< AssistantCardMetadata -> = async ({ http }) => { +> = async ({ http, application }) => { const allConnectors = await loadConnectors({ http }); + const { + capabilities: { actions }, + } = application; const aiConnectors = allConnectors.reduce((acc: AIConnector[], connector) => { if (!connector.isMissingSecrets && AllowedActionTypeIds.includes(connector.actionTypeId)) { @@ -37,6 +40,8 @@ export const checkAssistantCardComplete: OnboardingCardCheckComplete< completeBadgeText, metadata: { connectors: aiConnectors, + canExecuteConnectors: Boolean(actions?.show && actions?.execute), + canCreateConnectors: Boolean(actions?.save), }, }; }; diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/connectors/connector_cards.tsx b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/connectors/connector_cards.tsx index 3cdefaa1fe490..472459b631b0a 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/connectors/connector_cards.tsx +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/connectors/connector_cards.tsx @@ -15,69 +15,104 @@ import { EuiText, EuiBadge, EuiSpacer, + EuiCallOut, } from '@elastic/eui'; import { css } from '@emotion/css'; import { useKibana } from '../../../../../../common/lib/kibana'; import { CreateConnectorPopover } from './create_connector_popover'; import { ConnectorSetup } from './connector_setup'; +import * as i18n from './translations'; +import { MissingPrivilegesDescription } from './missing_privileges_tooltip'; interface ConnectorCardsProps { connectors?: AIConnector[]; onConnectorSaved: () => void; + canCreateConnectors?: boolean; } export const ConnectorCards = React.memo( - ({ connectors, onConnectorSaved }) => { + ({ connectors, onConnectorSaved, canCreateConnectors }) => { const { triggersActionsUi: { actionTypeRegistry }, } = useKibana().services; - if (!connectors) return ; + if (!connectors) { + return ; + } + + const hasConnectors = connectors.length > 0; - if (connectors.length > 0) { + // show callout when user is missing actions.save privilege + if (!hasConnectors && !canCreateConnectors) { return ( - <> - - {connectors.map((connector) => ( - - - - - {connector.name} - - - - {actionTypeRegistry.get(connector.actionTypeId).actionTypeTitle} - - - - - - ))} - - - - + + + ); } - return ; + return ( + <> + {hasConnectors ? ( + <> + + + + + ) : ( + + )} + + ); } ); ConnectorCards.displayName = 'ConnectorCards'; + +interface ConnectorListProps { + connectors: AIConnector[]; + actionTypeRegistry: ReturnType< + typeof useKibana + >['services']['triggersActionsUi']['actionTypeRegistry']; +} + +const ConnectorList = React.memo(({ connectors, actionTypeRegistry }) => ( + + {connectors.map((connector) => ( + + + + + {connector.name} + + + + {actionTypeRegistry.get(connector.actionTypeId).actionTypeTitle} + + + + + + ))} + +)); + +ConnectorList.displayName = 'ConnectorList'; diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/connectors/create_connector_popover.tsx b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/connectors/create_connector_popover.tsx index de432d6597afd..32bcd66f49249 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/connectors/create_connector_popover.tsx +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/connectors/create_connector_popover.tsx @@ -8,14 +8,16 @@ import React, { useCallback, useState } from 'react'; import { css } from '@emotion/css'; import { EuiPopover, EuiLink, EuiText } from '@elastic/eui'; import { ConnectorSetup } from './connector_setup'; -import * as i18n from '../translations'; +import * as i18n from './translations'; +import { MissingPrivilegesTooltip } from './missing_privileges_tooltip'; interface CreateConnectorPopoverProps { onConnectorSaved: () => void; + canCreateConnectors?: boolean; } export const CreateConnectorPopover = React.memo( - ({ onConnectorSaved }) => { + ({ onConnectorSaved, canCreateConnectors }) => { const [isOpen, setIsPopoverOpen] = useState(false); const closePopover = useCallback(() => setIsPopoverOpen(false), []); @@ -23,6 +25,15 @@ export const CreateConnectorPopover = React.memo( () => setIsPopoverOpen((isPopoverOpen) => !isPopoverOpen), [] ); + if (!canCreateConnectors) { + return ( + + + {i18n.ASSISTANT_CARD_CREATE_NEW_CONNECTOR_POPOVER} + + + ); + } return ( (({ children }) => ( + } + > + {children} + +)); +MissingPrivilegesTooltip.displayName = 'MissingPrivilegesTooltip'; + +export const MissingPrivilegesDescription = React.memo(() => { + return ( + + {i18n.PRIVILEGES_REQUIRED_TITLE} + + +

    +
  • {i18n.REQUIRED_PRIVILEGES_CONNECTORS_ALL}
  • +
+ + + {i18n.CONTACT_ADMINISTRATOR} + + ); +}); +MissingPrivilegesDescription.displayName = 'MissingPrivilegesDescription'; diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/connectors/translations.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/connectors/translations.ts new file mode 100644 index 0000000000000..983a6a67f5b32 --- /dev/null +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/connectors/translations.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const ASSISTANT_CARD_CREATE_NEW_CONNECTOR_POPOVER = i18n.translate( + 'xpack.securitySolution.onboarding.assistantCard.createNewConnectorPopover', + { + defaultMessage: 'Create new connector', + } +); + +export const PRIVILEGES_MISSING_TITLE = i18n.translate( + 'xpack.securitySolution.onboarding.assistantCard.missingPrivileges.title', + { + defaultMessage: 'Missing privileges', + } +); + +export const PRIVILEGES_REQUIRED_TITLE = i18n.translate( + 'xpack.securitySolution.onboarding.assistantCard.requiredPrivileges', + { + defaultMessage: 'The minimum Kibana privileges required to use this feature are:', + } +); + +export const REQUIRED_PRIVILEGES_CONNECTORS_ALL = i18n.translate( + 'xpack.securitySolution.onboarding.assistantCard.requiredPrivileges.connectorsAll', + { + defaultMessage: 'Management > Connectors: All', + } +); + +export const CONTACT_ADMINISTRATOR = i18n.translate( + 'xpack.securitySolution.onboarding.assistantCard.missingPrivileges.contactAdministrator', + { + defaultMessage: 'Contact your administrator for assistance.', + } +); diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/index.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/index.ts index 27deda4190f2e..fedf975052327 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/index.ts +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/index.ts @@ -27,6 +27,6 @@ export const assistantCardConfig: OnboardingCardConfig = checkComplete: checkAssistantCardComplete, // Both capabilities are needed for this card, so we should use a double array to create an AND conditional // (a single array would create an OR conditional between them) - capabilities: [['securitySolutionAssistant.ai-assistant', 'actions.show']], + capabilities: [['securitySolutionAssistant.ai-assistant']], licenseType: 'enterprise', }; diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/translations.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/translations.ts index 41e73bdacf061..de3c111280436 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/translations.ts +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/translations.ts @@ -42,3 +42,10 @@ export const ASSISTANT_CARD_CREATE_NEW_CONNECTOR_POPOVER = i18n.translate( defaultMessage: 'Create new connector', } ); + +export const PRIVILEGES_MISSING_TITLE = i18n.translate( + 'xpack.securitySolution.onboarding.assistantCard.callout.title', + { + defaultMessage: 'Missing privileges', + } +); diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/types.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/types.ts index f1e0216406391..d6647adf054fe 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/types.ts +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/types.ts @@ -9,4 +9,6 @@ import type { ActionConnector } from '@kbn/alerts-ui-shared'; export interface AssistantCardMetadata { connectors: ActionConnector[]; + canExecuteConnectors: boolean; + canCreateConnectors: boolean; } diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/card_content_image_panel.styles.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/card_content_image_panel.styles.ts index d8f6d6c278ee3..c7998135aa8ae 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/card_content_image_panel.styles.ts +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/card_content_image_panel.styles.ts @@ -6,13 +6,15 @@ */ import { css } from '@emotion/css'; -import { useEuiTheme, useEuiShadow } from '@elastic/eui'; +import { useEuiTheme, useEuiShadow, COLOR_MODES_STANDARD } from '@elastic/eui'; export const useCardContentImagePanelStyles = () => { - const { euiTheme } = useEuiTheme(); + const { euiTheme, colorMode } = useEuiTheme(); const shadowStyles = useEuiShadow('m'); + const isDarkMode = colorMode === COLOR_MODES_STANDARD.dark; return css` padding-top: 8px; + ${isDarkMode ? `background-color: ${euiTheme.colors.lightestShade}` : ''}; .cardSpacer { width: 8%; } diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/dashboards/dashboards_card.tsx b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/dashboards/dashboards_card.tsx index df98800d83f32..201aa4f0d3150 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/dashboards/dashboards_card.tsx +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/dashboards/dashboards_card.tsx @@ -6,7 +6,16 @@ */ import React, { useCallback, useMemo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiLink, + EuiSpacer, + EuiText, + useEuiTheme, + COLOR_MODES_STANDARD, +} from '@elastic/eui'; import { SecurityPageName } from '@kbn/security-solution-navigation'; import { OnboardingCardId } from '../../../../constants'; import type { OnboardingCardComponent } from '../../../../types'; @@ -21,6 +30,9 @@ export const DashboardsCard: OnboardingCardComponent = ({ setComplete, setExpandedCardId, }) => { + const { colorMode } = useEuiTheme(); + const isDarkMode = colorMode === COLOR_MODES_STANDARD.dark; + const isIntegrationsCardComplete = useMemo( () => isCardComplete(OnboardingCardId.integrations), [isCardComplete] @@ -42,7 +54,11 @@ export const DashboardsCard: OnboardingCardComponent = ({ alignItems="flexStart" > - + {i18n.DASHBOARDS_CARD_DESCRIPTION} {!isIntegrationsCardComplete && ( diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/rules/rules_card.tsx b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/rules/rules_card.tsx index 7f283c0ffbc78..50c722d49c359 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/rules/rules_card.tsx +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/rules/rules_card.tsx @@ -6,7 +6,16 @@ */ import React, { useCallback, useMemo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiLink, + EuiSpacer, + EuiText, + useEuiTheme, + COLOR_MODES_STANDARD, +} from '@elastic/eui'; import { SecurityPageName } from '@kbn/security-solution-navigation'; import { SecuritySolutionLinkButton } from '../../../../../common/components/links'; import { OnboardingCardId } from '../../../../constants'; @@ -17,6 +26,9 @@ import rulesImageSrc from './images/rules.png'; import * as i18n from './translations'; export const RulesCard: OnboardingCardComponent = ({ isCardComplete, setExpandedCardId }) => { + const { colorMode } = useEuiTheme(); + const isDarkMode = colorMode === COLOR_MODES_STANDARD.dark; + const isIntegrationsCardComplete = useMemo( () => isCardComplete(OnboardingCardId.integrations), [isCardComplete] @@ -35,7 +47,11 @@ export const RulesCard: OnboardingCardComponent = ({ isCardComplete, setExpanded alignItems="flexStart" > - + {i18n.RULES_CARD_DESCRIPTION} {!isIntegrationsCardComplete && ( diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/constants.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/constants.ts index f67b991e0ea75..722dfad89fc5b 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/constants.ts +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/constants.ts @@ -8,7 +8,6 @@ export const TELEMETRY_FOOTER_LINK = `footer_link`; export enum OnboardingFooterLinkItemId { - video = 'video', documentation = 'documentation', demo = 'demo', forum = 'forum', diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/footer_items.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/footer_items.ts index f064947f657a4..40a6e01f6c037 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/footer_items.ts +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/footer_items.ts @@ -5,78 +5,90 @@ * 2.0. */ import { i18n } from '@kbn/i18n'; -import documentation from './images/documentation.png'; -import forum from './images/forum.png'; -import demo from './images/demo.png'; -import labs from './images/labs.png'; +import { COLOR_MODES_STANDARD, useEuiTheme } from '@elastic/eui'; +import documentationImage from './images/documentation.png'; +import darkDocumentationImage from './images/documentation_dark.png'; +import forumImage from './images/forum.png'; +import darkForumImge from './images/forum_dark.png'; +import demoImage from './images/demo.png'; +import darkDemoImage from './images/demo_dark.png'; +import labsImage from './images/labs.png'; +import darkLabsImage from './images/labs_dark.png'; import { OnboardingFooterLinkItemId } from './constants'; -export const footerItems = [ - { - icon: documentation, - id: OnboardingFooterLinkItemId.documentation, - title: i18n.translate('xpack.securitySolution.onboarding.footer.documentation.title', { - defaultMessage: 'Browse documentation', - }), - description: i18n.translate( - 'xpack.securitySolution.onboarding.footer.documentation.description', - { - defaultMessage: 'In-depth guides on all Elastic features', - } - ), - link: { - title: i18n.translate('xpack.securitySolution.onboarding.footer.documentation.link.title', { - defaultMessage: 'Start reading', +export const useFooterItems = () => { + const { colorMode } = useEuiTheme(); + const isDarkMode = colorMode === COLOR_MODES_STANDARD.dark; + + const footerItems = [ + { + icon: isDarkMode ? darkDocumentationImage : documentationImage, + id: OnboardingFooterLinkItemId.documentation, + title: i18n.translate('xpack.securitySolution.onboarding.footer.documentation.title', { + defaultMessage: 'Browse documentation', }), - href: 'https://docs.elastic.co/integrations/elastic-security-intro', + description: i18n.translate( + 'xpack.securitySolution.onboarding.footer.documentation.description', + { + defaultMessage: 'In-depth guides on all Elastic features', + } + ), + link: { + title: i18n.translate('xpack.securitySolution.onboarding.footer.documentation.link.title', { + defaultMessage: 'Start reading', + }), + href: 'https://docs.elastic.co/integrations/elastic-security-intro', + }, }, - }, - { - icon: forum, - id: OnboardingFooterLinkItemId.forum, - title: i18n.translate('xpack.securitySolution.onboarding.footer.forum.title', { - defaultMessage: 'Explore forum', - }), - description: i18n.translate('xpack.securitySolution.onboarding.footer.forum.description', { - defaultMessage: 'Exchange thoughts about Elastic', - }), - link: { - title: i18n.translate('xpack.securitySolution.onboarding.footer.forum.link.title', { - defaultMessage: 'Discuss Forum', + { + icon: isDarkMode ? darkForumImge : forumImage, + id: OnboardingFooterLinkItemId.forum, + title: i18n.translate('xpack.securitySolution.onboarding.footer.forum.title', { + defaultMessage: 'Explore forum', + }), + description: i18n.translate('xpack.securitySolution.onboarding.footer.forum.description', { + defaultMessage: 'Exchange thoughts about Elastic', }), - href: 'https://discuss.elastic.co/c/security/83', + link: { + title: i18n.translate('xpack.securitySolution.onboarding.footer.forum.link.title', { + defaultMessage: 'Discuss Forum', + }), + href: 'https://discuss.elastic.co/c/security/83', + }, }, - }, - { - icon: demo, - id: OnboardingFooterLinkItemId.demo, - title: i18n.translate('xpack.securitySolution.onboarding.footer.demo.title', { - defaultMessage: 'View demo project', - }), - description: i18n.translate('xpack.securitySolution.onboarding.footer.demo.description', { - defaultMessage: 'Discover Elastic using sample data', - }), - link: { - title: i18n.translate('xpack.securitySolution.onboarding.footer.demo.link.title', { - defaultMessage: 'Explore demo', + { + icon: isDarkMode ? darkDemoImage : demoImage, + id: OnboardingFooterLinkItemId.demo, + title: i18n.translate('xpack.securitySolution.onboarding.footer.demo.title', { + defaultMessage: 'View demo project', + }), + description: i18n.translate('xpack.securitySolution.onboarding.footer.demo.description', { + defaultMessage: 'Discover Elastic using sample data', }), - href: 'https://www.elastic.co/demo-gallery?solutions=security&features=null', + link: { + title: i18n.translate('xpack.securitySolution.onboarding.footer.demo.link.title', { + defaultMessage: 'Explore demo', + }), + href: 'https://www.elastic.co/demo-gallery?solutions=security&features=null', + }, }, - }, - { - icon: labs, - id: OnboardingFooterLinkItemId.labs, - title: i18n.translate('xpack.securitySolution.onboarding.footer.labs.title', { - defaultMessage: 'Elastic Security Labs', - }), - description: i18n.translate('xpack.securitySolution.onboarding.footer.labs.description', { - defaultMessage: 'Insights from security researchers', - }), - link: { - title: i18n.translate('xpack.securitySolution.onboarding.footer.labs.link.title', { - defaultMessage: 'Learn more', + { + icon: isDarkMode ? darkLabsImage : labsImage, + id: OnboardingFooterLinkItemId.labs, + title: i18n.translate('xpack.securitySolution.onboarding.footer.labs.title', { + defaultMessage: 'Elastic Security Labs', }), - href: 'https://www.elastic.co/security-labs', + description: i18n.translate('xpack.securitySolution.onboarding.footer.labs.description', { + defaultMessage: 'Insights from security researchers', + }), + link: { + title: i18n.translate('xpack.securitySolution.onboarding.footer.labs.link.title', { + defaultMessage: 'Learn more', + }), + href: 'https://www.elastic.co/security-labs', + }, }, - }, -] as const; + ] as const; + + return footerItems; +}; diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/images/demo_dark.png b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/images/demo_dark.png new file mode 100644 index 0000000000000..fb90f857c0694 Binary files /dev/null and b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/images/demo_dark.png differ diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/images/documentation_dark.png b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/images/documentation_dark.png new file mode 100644 index 0000000000000..8692b53f96ea5 Binary files /dev/null and b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/images/documentation_dark.png differ diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/images/forum_dark.png b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/images/forum_dark.png new file mode 100644 index 0000000000000..3ede58eeeaa3a Binary files /dev/null and b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/images/forum_dark.png differ diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/images/labs_dark.png b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/images/labs_dark.png new file mode 100644 index 0000000000000..846137fe4187b Binary files /dev/null and b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/images/labs_dark.png differ diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/onboarding_footer.tsx b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/onboarding_footer.tsx index fdf743d339a60..125d2af118d3f 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/onboarding_footer.tsx +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_footer/onboarding_footer.tsx @@ -8,13 +8,14 @@ import React, { useCallback } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; import { useFooterStyles } from './onboarding_footer.styles'; -import { footerItems } from './footer_items'; +import { useFooterItems } from './footer_items'; import { trackOnboardingLinkClick } from '../../common/lib/telemetry'; import type { OnboardingFooterLinkItemId } from './constants'; import { TELEMETRY_FOOTER_LINK } from './constants'; export const OnboardingFooter = React.memo(() => { const styles = useFooterStyles(); + const footerItems = useFooterItems(); return ( {footerItems.map(({ id, title, icon, description, link }) => ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/notes/old_notes.tsx b/x-pack/plugins/security_solution/public/timelines/components/notes/old_notes.tsx index 71432267ac0da..09c45d887b373 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/notes/old_notes.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/notes/old_notes.tsx @@ -111,7 +111,7 @@ interface NotesTabContentProps { } /** - * Renders the "old" notes tab content. This should be removed when we remove the securitySolutionNotesEnabled feature flag + * Renders the "old" notes tab content. This should be removed when we remove the securitySolutionNotesDisabled feature flag */ export const OldNotes: React.FC = React.memo(({ timelineId }) => { const dispatch = useDispatch(); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.tsx index f83d783667e4b..602d2353f342f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.tsx @@ -117,8 +117,8 @@ export const EqlTabContentComponent: React.FC = ({ }); const { openFlyout } = useExpandableFlyoutApi(); - const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled( - 'securitySolutionNotesEnabled' + const securitySolutionNotesDisabled = useIsExperimentalFeatureEnabled( + 'securitySolutionNotesDisabled' ); const { @@ -139,7 +139,7 @@ export const EqlTabContentComponent: React.FC = ({ const onToggleShowNotes = useCallback( (eventId?: string) => { const indexName = selectedPatterns.join(','); - if (eventId && securitySolutionNotesEnabled) { + if (eventId && !securitySolutionNotesDisabled) { openFlyout({ right: { id: DocumentDetailsRightPanelKey, @@ -177,7 +177,7 @@ export const EqlTabContentComponent: React.FC = ({ }, [ openFlyout, - securitySolutionNotesEnabled, + securitySolutionNotesDisabled, selectedPatterns, telemetry, timelineId, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/index.tsx index 601a31d97fa5c..40d4f939b13fd 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/index.tsx @@ -253,8 +253,8 @@ const TabsContentComponent: React.FC = ({ selectTimelineESQLSavedSearchId(state, timelineId) ); - const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled( - 'securitySolutionNotesEnabled' + const securitySolutionNotesDisabled = useIsExperimentalFeatureEnabled( + 'securitySolutionNotesDisabled' ); const [visualizationInFlyoutEnabled] = useUiSetting$( @@ -320,16 +320,20 @@ const TabsContentComponent: React.FC = ({ } }, [fetchNotes, isTimelineSaved]); - const numberOfNotesNewSystem = useSelector((state: State) => + const notesNewSystem = useSelector((state: State) => selectSortedNotesBySavedObjectId(state, { savedObjectId: timelineSavedObjectId, sort: { field: 'created', direction: 'asc' }, }) ); + const numberOfNotesNewSystem = useMemo( + () => notesNewSystem.length + (isEmpty(timelineDescription) ? 0 : 1), + [notesNewSystem, timelineDescription] + ); const numberOfNotes = useMemo( - () => (securitySolutionNotesEnabled ? numberOfNotesNewSystem.length : numberOfNotesOldSystem), - [numberOfNotesNewSystem, numberOfNotesOldSystem, securitySolutionNotesEnabled] + () => (securitySolutionNotesDisabled ? numberOfNotesOldSystem : numberOfNotesNewSystem), + [numberOfNotesNewSystem, numberOfNotesOldSystem, securitySolutionNotesDisabled] ); const setActiveTab = useCallback( @@ -446,9 +450,7 @@ const TabsContentComponent: React.FC = ({ > {i18n.NOTES_TAB} {showTimeline && numberOfNotes > 0 && timelineType === TimelineTypeEnum.default && ( -
- {numberOfNotes} -
+ {numberOfNotes} )} = ({ {showTimeline && numberOfPinnedEvents > 0 && timelineType === TimelineTypeEnum.default && ( -
- {numberOfPinnedEvents} -
+ {numberOfPinnedEvents} )}
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.test.tsx index 4de3728e2290f..452dc1620b946 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.test.tsx @@ -70,14 +70,14 @@ const mockGlobalStateWithUnSavedTimeline: State = { describe('NotesTabContentComponent', () => { beforeEach(() => { jest.clearAllMocks(); - (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true); + (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false); (useUserPrivileges as jest.Mock).mockReturnValue({ kibanaSecuritySolutionsPrivileges: { crud: true }, }); }); it('should show the old note system', () => { - (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false); + (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true); const { getByTestId, queryByTestId } = render( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.tsx index aad74f15d4f62..58522d32dd55f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.tsx @@ -71,7 +71,7 @@ interface NotesTabContentProps { /** * Renders the notes tab content. - * At this time the component support the old notes system and the new notes system (via the securitySolutionNotesEnabled feature flag). + * At this time the component support the old notes system and the new notes system (via the securitySolutionNotesDisabled feature flag). * The old notes system is deprecated and will be removed in the future. * In both cases, the component fetches the notes for the timeline and renders: * - the timeline description @@ -86,8 +86,8 @@ const NotesTabContentComponent: React.FC = React.memo(({ t const { kibanaSecuritySolutionsPrivileges } = useUserPrivileges(); const canCreateNotes = kibanaSecuritySolutionsPrivileges.crud; - const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled( - 'securitySolutionNotesEnabled' + const securitySolutionNotesDisabled = useIsExperimentalFeatureEnabled( + 'securitySolutionNotesDisabled' ); const getScrollToTop = useMemo(() => getScrollToTopSelector(), []); @@ -180,7 +180,9 @@ const NotesTabContentComponent: React.FC = React.memo(({ t
- {securitySolutionNotesEnabled ? ( + {securitySolutionNotesDisabled ? ( + + ) : ( {timelineDescription} @@ -213,8 +215,6 @@ const NotesTabContentComponent: React.FC = React.memo(({ t - ) : ( - )} diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned/index.tsx index fb488d7c6d21b..a12f3bb9fd530 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned/index.tsx @@ -146,8 +146,8 @@ export const PinnedTabContentComponent: React.FC = ({ }); const { openFlyout } = useExpandableFlyoutApi(); - const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled( - 'securitySolutionNotesEnabled' + const securitySolutionNotesDisabled = useIsExperimentalFeatureEnabled( + 'securitySolutionNotesDisabled' ); const { @@ -168,7 +168,7 @@ export const PinnedTabContentComponent: React.FC = ({ const onToggleShowNotes = useCallback( (eventId?: string) => { const indexName = selectedPatterns.join(','); - if (eventId && securitySolutionNotesEnabled) { + if (eventId && !securitySolutionNotesDisabled) { openFlyout({ right: { id: DocumentDetailsRightPanelKey, @@ -206,7 +206,7 @@ export const PinnedTabContentComponent: React.FC = ({ }, [ openFlyout, - securitySolutionNotesEnabled, + securitySolutionNotesDisabled, selectedPatterns, telemetry, timelineId, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.test.tsx index 493fdb4bc603e..70afec0d73135 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.test.tsx @@ -882,12 +882,12 @@ describe('query tab with unified timeline', () => { }); describe('Leading actions - notes', () => { - describe('securitySolutionNotesEnabled = true', () => { + describe('securitySolutionNotesDisabled = false', () => { beforeEach(() => { (useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation( jest.fn((feature: keyof ExperimentalFeatures) => { - if (feature === 'securitySolutionNotesEnabled') { - return true; + if (feature === 'securitySolutionNotesDisabled') { + return false; } return allowedExperimentalValues[feature]; }) @@ -937,12 +937,12 @@ describe('query tab with unified timeline', () => { ); }); - describe('securitySolutionNotesEnabled = false', () => { + describe('securitySolutionNotesDisabled = true', () => { beforeEach(() => { (useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation( jest.fn((feature: keyof ExperimentalFeatures) => { - if (feature === 'securitySolutionNotesEnabled') { - return false; + if (feature === 'securitySolutionNotesDisabled') { + return true; } return allowedExperimentalValues[feature]; }) @@ -1071,12 +1071,12 @@ describe('query tab with unified timeline', () => { }); describe('Leading actions - pin', () => { - describe('securitySolutionNotesEnabled = true', () => { + describe('securitySolutionNotesDisabled = false', () => { beforeEach(() => { (useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation( jest.fn((feature: keyof ExperimentalFeatures) => { - if (feature === 'securitySolutionNotesEnabled') { - return true; + if (feature === 'securitySolutionNotesDisabled') { + return false; } return allowedExperimentalValues[feature]; }) @@ -1155,12 +1155,12 @@ describe('query tab with unified timeline', () => { ); }); - describe('securitySolutionNotesEnabled = false', () => { + describe('securitySolutionNotesDisabled = true', () => { beforeEach(() => { (useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation( jest.fn((feature: keyof ExperimentalFeatures) => { - if (feature === 'securitySolutionNotesEnabled') { - return false; + if (feature === 'securitySolutionNotesDisabled') { + return true; } return allowedExperimentalValues[feature]; }) diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.tsx index d12c213b7f3e2..8ea1db39a3618 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.tsx @@ -184,8 +184,8 @@ export const QueryTabContentComponent: React.FC = ({ }); const { openFlyout } = useExpandableFlyoutApi(); - const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled( - 'securitySolutionNotesEnabled' + const securitySolutionNotesDisabled = useIsExperimentalFeatureEnabled( + 'securitySolutionNotesDisabled' ); const { @@ -206,7 +206,7 @@ export const QueryTabContentComponent: React.FC = ({ const onToggleShowNotes = useCallback( (eventId?: string) => { const indexName = selectedPatterns.join(','); - if (eventId && securitySolutionNotesEnabled) { + if (eventId && !securitySolutionNotesDisabled) { openFlyout({ right: { id: DocumentDetailsRightPanelKey, @@ -244,7 +244,7 @@ export const QueryTabContentComponent: React.FC = ({ }, [ openFlyout, - securitySolutionNotesEnabled, + securitySolutionNotesDisabled, selectedPatterns, telemetry, timelineId, 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 92c13a521ed2c..a07823194fa69 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 @@ -21,10 +21,12 @@ import type { GetAgentsResponse, GetInfoResponse, GetOneAgentPolicyResponse, + GetOnePackagePolicyResponse, GetPackagePoliciesRequest, GetPackagePoliciesResponse, PackagePolicy, PostFleetSetupResponse, + UpdatePackagePolicyResponse, } from '@kbn/fleet-plugin/common'; import { AGENT_API_ROUTES, @@ -39,6 +41,7 @@ import { PACKAGE_POLICY_API_ROUTES, PACKAGE_POLICY_SAVED_OBJECT_TYPE, SETUP_API_ROUTE, + packagePolicyRouteService, } from '@kbn/fleet-plugin/common'; import type { ToolingLog } from '@kbn/tooling-log'; import type { KbnClient } from '@kbn/test'; @@ -57,11 +60,14 @@ import type { GetEnrollmentAPIKeysResponse, GetOutputsResponse, PostAgentUnenrollResponse, + UpdateAgentPolicyRequest, + UpdateAgentPolicyResponse, } from '@kbn/fleet-plugin/common/types'; import semver from 'semver'; import axios from 'axios'; import { userInfo } from 'os'; import pRetry from 'p-retry'; +import { getPolicyDataForUpdate } from '../../../common/endpoint/service/policy'; import { fetchActiveSpace } from './spaces'; import { fetchKibanaStatus } from '../../../common/endpoint/utils/kibana_status'; import { isFleetServerRunning } from './fleet_server/fleet_server_services'; @@ -76,6 +82,7 @@ import { } from '../../../common/endpoint/data_loaders/utils'; import { catchAxiosErrorFormatAndThrow } from '../../../common/endpoint/format_axios_error'; import { FleetAgentGenerator } from '../../../common/endpoint/data_generators/fleet_agent_generator'; +import type { PolicyData } from '../../../common/endpoint/types'; const fleetGenerator = new FleetAgentGenerator(); const CURRENT_USERNAME = userInfo().username.toLowerCase(); @@ -101,6 +108,39 @@ export const randomAgentPolicyName = (() => { */ const isValidArtifactVersion = (version: string) => !!version.match(/^\d+\.\d+\.\d+(-SNAPSHOT)?$/); +const getAgentPolicyDataForUpdate = ( + agentPolicy: AgentPolicy +): UpdateAgentPolicyRequest['body'] => { + return pick(agentPolicy, [ + 'advanced_settings', + 'agent_features', + 'data_output_id', + 'description', + 'download_source_id', + 'fleet_server_host_id', + 'global_data_tags', + 'has_fleet_server', + 'id', + 'inactivity_timeout', + 'is_default', + 'is_default_fleet_server', + 'is_managed', + 'is_protected', + 'keep_monitoring_alive', + 'monitoring_diagnostics', + 'monitoring_enabled', + 'monitoring_http', + 'monitoring_output_id', + 'monitoring_pprof_enabled', + 'name', + 'namespace', + 'overrides', + 'space_ids', + 'supports_agentless', + 'unenroll_timeout', + ]) as UpdateAgentPolicyRequest['body']; +}; + export const checkInFleetAgent = async ( esClient: Client, agentId: string, @@ -1369,3 +1409,93 @@ export const enableFleetSpaceAwareness = memoize(async (kbnClient: KbnClient): P }) .catch(catchAxiosErrorFormatAndThrow); }); + +/** + * Fetches a single integratino policy by id + * @param kbnClient + * @param policyId + */ +export const fetchIntegrationPolicy = async ( + kbnClient: KbnClient, + policyId: string +): Promise => { + return kbnClient + .request({ + path: packagePolicyRouteService.getInfoPath(policyId), + method: 'GET', + headers: { 'elastic-api-version': '2023-10-31' }, + }) + .catch(catchAxiosErrorFormatAndThrow) + .then((response) => response.data.item); +}; + +/** + * Update a fleet integration policy (aka: package policy) + * @param kbnClient + */ +export const updateIntegrationPolicy = async ( + kbnClient: KbnClient, + /** The Integration policy id */ + id: string, + policyData: Partial, + /** If set to `true`, then `policyData` can be a partial set of updates and not the full policy data */ + patch: boolean = false +): Promise => { + let fullPolicyData = policyData; + + if (patch) { + const currentSavedPolicy = await fetchIntegrationPolicy(kbnClient, id); + fullPolicyData = getPolicyDataForUpdate(currentSavedPolicy as PolicyData); + Object.assign(fullPolicyData, policyData); + } + + return kbnClient + .request({ + path: packagePolicyRouteService.getUpdatePath(id), + method: 'PUT', + body: fullPolicyData, + headers: { 'elastic-api-version': '2023-10-31' }, + }) + .catch(catchAxiosErrorFormatAndThrow) + .then((response) => response.data.item); +}; + +/** + * Updates a Fleet agent policy + * @param kbnClient + * @param id + * @param policyData + * @param patch + */ +export const updateAgentPolicy = async ( + kbnClient: KbnClient, + /** Fleet Agent Policy ID */ + id: string, + /** The updated agent policy data. Could be a `partial` update if `patch` arguments below is true */ + policyData: Partial, + /** + * If set to `true`, the `policyData` provided on input will first be merged with the latest version + * of the policy and then the updated applied + */ + patch: boolean = false +): Promise => { + let fullPolicyData = policyData; + + if (patch) { + const currentSavedPolicy = await fetchAgentPolicy(kbnClient, id); + + fullPolicyData = getAgentPolicyDataForUpdate(currentSavedPolicy); + delete fullPolicyData.id; + Object.assign(fullPolicyData, policyData); + } + + return kbnClient + .request({ + path: agentPolicyRouteService.getUpdatePath(id), + method: 'PUT', + body: fullPolicyData, + headers: { 'elastic-api-version': '2023-10-31' }, + }) + .catch(catchAxiosErrorFormatAndThrow) + .then((response) => response.data.item); +}; diff --git a/x-pack/plugins/security_solution/server/assistant/tools/esql/nl_to_esql_tool.ts b/x-pack/plugins/security_solution/server/assistant/tools/esql/nl_to_esql_tool.ts index 1205fb03b0458..11ca8ffd94edd 100644 --- a/x-pack/plugins/security_solution/server/assistant/tools/esql/nl_to_esql_tool.ts +++ b/x-pack/plugins/security_solution/server/assistant/tools/esql/nl_to_esql_tool.ts @@ -49,11 +49,7 @@ export const NL_TO_ESQL_TOOL: AssistantTool = { connectorId, input: question, ...(isOssModel ? { functionCalling: 'simulated' } : {}), - logger: { - debug: (source) => { - logger.debug(typeof source === 'function' ? source() : source); - }, - }, + logger, }) ); }; diff --git a/x-pack/plugins/security_solution/server/config.mock.ts b/x-pack/plugins/security_solution/server/config.mock.ts index 1d0d31e9387e2..5fb3dc7b3b48d 100644 --- a/x-pack/plugins/security_solution/server/config.mock.ts +++ b/x-pack/plugins/security_solution/server/config.mock.ts @@ -10,6 +10,7 @@ import type { ExperimentalFeatures } from '../common/experimental_features'; import { parseExperimentalConfigValue } from '../common/experimental_features'; import { getDefaultConfigSettings } from '../common/config_settings'; import type { ConfigType } from './config'; +import { duration } from 'moment'; export const createMockConfig = (): ConfigType => { const enableExperimental: Array = ['responseActionUploadEnabled']; @@ -45,6 +46,8 @@ export const createMockConfig = (): ConfigType => { }, }, entityStore: { + frequency: duration('1m'), + syncDelay: duration('5m'), developer: { pipelineDebugMode: false, }, diff --git a/x-pack/plugins/security_solution/server/config.ts b/x-pack/plugins/security_solution/server/config.ts index 1265aa4c25749..240e452cd44bc 100644 --- a/x-pack/plugins/security_solution/server/config.ts +++ b/x-pack/plugins/security_solution/server/config.ts @@ -176,6 +176,8 @@ export const configSchema = schema.object({ }), }), entityStore: schema.object({ + syncDelay: schema.duration({ defaultValue: '60s' }), + frequency: schema.duration({ defaultValue: '60s' }), developer: schema.object({ pipelineDebugMode: schema.boolean({ defaultValue: false }), }), diff --git a/x-pack/plugins/security_solution/server/endpoint/migrations/ensure_indices_exists_for_policies.test.ts b/x-pack/plugins/security_solution/server/endpoint/migrations/ensure_indices_exists_for_policies.test.ts new file mode 100644 index 0000000000000..b167997b68dda --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/migrations/ensure_indices_exists_for_policies.test.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 { createMockEndpointAppContextService } from '../mocks'; +import { ensureIndicesExistsForPolicies } from './ensure_indices_exists_for_policies'; +import { createPolicyDataStreamsIfNeeded as _createPolicyDataStreamsIfNeeded } from '../../fleet_integration/handlers/create_policy_datastreams'; + +jest.mock('../../fleet_integration/handlers/create_policy_datastreams'); +const createPolicyDataStreamsIfNeededMock = + _createPolicyDataStreamsIfNeeded as unknown as jest.Mock; + +describe('Ensure indices exists for policies migration', () => { + let endpointAppContextServicesMock: ReturnType; + + beforeEach(() => { + endpointAppContextServicesMock = createMockEndpointAppContextService(); + + ( + endpointAppContextServicesMock.getInternalFleetServices().packagePolicy.listIds as jest.Mock + ).mockResolvedValue({ + items: ['foo-1', 'foo-2', 'foo-3'], + }); + }); + + it('should query fleet looking for all endpoint integration policies', async () => { + const fleetServicesMock = endpointAppContextServicesMock.getInternalFleetServices(); + await ensureIndicesExistsForPolicies(endpointAppContextServicesMock); + + expect(fleetServicesMock.packagePolicy.listIds).toHaveBeenCalledWith(expect.anything(), { + kuery: fleetServicesMock.endpointPolicyKuery, + perPage: 10000, + }); + }); + + it('should call createPolicyDataStreamsIfNeeded() with list of existing policies', async () => { + await ensureIndicesExistsForPolicies(endpointAppContextServicesMock); + + expect(createPolicyDataStreamsIfNeededMock).toHaveBeenCalledWith({ + endpointServices: endpointAppContextServicesMock, + endpointPolicyIds: ['foo-1', 'foo-2', 'foo-3'], + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/endpoint/migrations/ensure_indices_exists_for_policies.ts b/x-pack/plugins/security_solution/server/endpoint/migrations/ensure_indices_exists_for_policies.ts new file mode 100644 index 0000000000000..778a333fc4818 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/migrations/ensure_indices_exists_for_policies.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 { createPolicyDataStreamsIfNeeded } from '../../fleet_integration/handlers/create_policy_datastreams'; +import type { EndpointAppContextService } from '../endpoint_app_context_services'; + +export const ensureIndicesExistsForPolicies = async ( + endpointServices: EndpointAppContextService +): Promise => { + const logger = endpointServices.createLogger('startupPolicyIndicesChecker'); + + const fleetServices = endpointServices.getInternalFleetServices(); + const soClient = fleetServices.savedObjects.createInternalUnscopedSoClient(); + const endpointPoliciesIds = await fleetServices.packagePolicy.listIds(soClient, { + kuery: fleetServices.endpointPolicyKuery, + perPage: 10000, + }); + + logger.info( + `Checking to ensure [${endpointPoliciesIds.items.length}] endpoint policies have backing indices` + ); + + await createPolicyDataStreamsIfNeeded({ + endpointServices, + endpointPolicyIds: endpointPoliciesIds.items, + }); +}; diff --git a/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts index 91a2bc40454b9..03c2e7e857e10 100644 --- a/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts @@ -76,6 +76,7 @@ import type { EndpointAuthz } from '../../../common/endpoint/types/authz'; import { createLicenseServiceMock } from '../../../common/license/mocks'; import { createFeatureUsageServiceMock } from '../services/feature_usage/mocks'; import { createProductFeaturesServiceMock } from '../../lib/product_features_service/mocks'; +import type { ConfigType } from '../../config'; /** * Creates a mocked EndpointAppContext. @@ -163,11 +164,15 @@ export const createMockEndpointAppContextServiceSetupContract = }; }; +type CreateMockEndpointAppContextServiceStartContractType = Omit< + DeeplyMockedKeys, + 'config' +> & { config: ConfigType }; // DeeplyMockedKeys doesn't support moment.Duration /** * Creates a mocked input contract for the `EndpointAppContextService#start()` method */ export const createMockEndpointAppContextServiceStartContract = - (): DeeplyMockedKeys => { + (): CreateMockEndpointAppContextServiceStartContractType => { const config = createMockConfig(); const logger = loggingSystemMock.create().get('mock_endpoint_app_context'); @@ -189,7 +194,7 @@ export const createMockEndpointAppContextServiceStartContract = securityMock.createMockAuthenticatedUser({ roles: ['superuser'] }) ); - const startContract: DeeplyMockedKeys = { + const startContract: CreateMockEndpointAppContextServiceStartContractType = { security, config, productFeaturesService: createProductFeaturesServiceMock( diff --git a/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_policy_datastreams.ts b/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_policy_datastreams.ts index 93fec3526a7b3..e94bc71cd4fa7 100644 --- a/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_policy_datastreams.ts +++ b/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_policy_datastreams.ts @@ -6,33 +6,18 @@ */ import pMap from 'p-map'; +import { buildIndexNameWithNamespace } from '../../../common/endpoint/utils/index_name_utilities'; import type { EndpointAppContextService } from '../../endpoint/endpoint_app_context_services'; import { catchAndWrapError } from '../../endpoint/utils'; import type { SimpleMemCacheInterface } from '../../endpoint/lib/simple_mem_cache'; import { SimpleMemCache } from '../../endpoint/lib/simple_mem_cache'; import { + DEFAULT_DIAGNOSTIC_INDEX_PATTERN, ENDPOINT_ACTION_RESPONSES_DS, ENDPOINT_HEARTBEAT_INDEX_PATTERN, } from '../../../common/endpoint/constants'; -import { DEFAULT_DIAGNOSTIC_INDEX } from '../../lib/telemetry/constants'; import { stringify } from '../../endpoint/utils/stringify'; -const buildIndexNameWithNamespace = ( - indexNamePrefixOrPattern: string, - namespace: string -): string => { - if (indexNamePrefixOrPattern.endsWith('*')) { - const hasDash = indexNamePrefixOrPattern.endsWith('-*'); - return `${indexNamePrefixOrPattern.substring(0, indexNamePrefixOrPattern.length - 1)}${ - hasDash ? '' : '-' - }${namespace}`; - } - - return `${indexNamePrefixOrPattern}${ - indexNamePrefixOrPattern.endsWith('-') ? '' : '-' - }${namespace}`; -}; - const cache = new SimpleMemCache({ // Cache of created Datastreams last for 12h, at which point it is checked again. // This is just a safeguard case (for whatever reason) the index is deleted @@ -78,22 +63,20 @@ export const createPolicyDataStreamsIfNeeded: PolicyDataStreamsCreator = async ( }); const indexesCreated: string[] = []; const createErrors: string[] = []; - const indicesToCreate: string[] = Object.values(policyNamespaces.integrationPolicy).reduce< - string[] - >((acc, namespaceList) => { - for (const namespace of namespaceList) { - acc.push( - buildIndexNameWithNamespace(DEFAULT_DIAGNOSTIC_INDEX, namespace), - buildIndexNameWithNamespace(ENDPOINT_ACTION_RESPONSES_DS, namespace) - ); - - if (endpointServices.isServerless()) { - acc.push(buildIndexNameWithNamespace(ENDPOINT_HEARTBEAT_INDEX_PATTERN, namespace)); + const indicesToCreate: string[] = Array.from( + Object.values(policyNamespaces.integrationPolicy).reduce>((acc, namespaceList) => { + for (const namespace of namespaceList) { + acc.add(buildIndexNameWithNamespace(DEFAULT_DIAGNOSTIC_INDEX_PATTERN, namespace)); + acc.add(buildIndexNameWithNamespace(ENDPOINT_ACTION_RESPONSES_DS, namespace)); + + if (endpointServices.isServerless()) { + acc.add(buildIndexNameWithNamespace(ENDPOINT_HEARTBEAT_INDEX_PATTERN, namespace)); + } } - } - return acc; - }, []); + return acc; + }, new Set()) + ); const processesDatastreamIndex = async (datastreamIndexName: string): Promise => { if (cache.get(datastreamIndexName)) { diff --git a/x-pack/plugins/security_solution/server/integration_tests/lib/telemetry_helpers.ts b/x-pack/plugins/security_solution/server/integration_tests/lib/telemetry_helpers.ts index d83ff1e910ca5..a36d5e1be38de 100644 --- a/x-pack/plugins/security_solution/server/integration_tests/lib/telemetry_helpers.ts +++ b/x-pack/plugins/security_solution/server/integration_tests/lib/telemetry_helpers.ts @@ -30,6 +30,7 @@ import { packagePolicyService } from '@kbn/fleet-plugin/server/services'; import { ENDPOINT_ARTIFACT_LISTS } from '@kbn/securitysolution-list-constants'; import { DETECTION_TYPE, NAMESPACE_TYPE } from '@kbn/lists-plugin/common/constants.mock'; +import { DEFAULT_DIAGNOSTIC_INDEX_PATTERN } from '../../../common/endpoint/constants'; import { bulkInsert, updateTimestamps } from './helpers'; import { TelemetryEventsSender } from '../../lib/telemetry/sender'; import type { @@ -40,7 +41,6 @@ import type { SecurityTelemetryTask } from '../../lib/telemetry/task'; import { Plugin as SecuritySolutionPlugin } from '../../plugin'; import { AsyncTelemetryEventsSender } from '../../lib/telemetry/async_sender'; import { type ITelemetryReceiver, TelemetryReceiver } from '../../lib/telemetry/receiver'; -import { DEFAULT_DIAGNOSTIC_INDEX } from '../../lib/telemetry/constants'; import mockEndpointAlert from '../__mocks__/endpoint-alert.json'; import mockedRule from '../__mocks__/rule.json'; import fleetAgents from '../__mocks__/fleet-agents.json'; @@ -147,7 +147,7 @@ export function getTelemetryTask( } export async function createMockedEndpointAlert(esClient: ElasticsearchClient) { - const index = `${DEFAULT_DIAGNOSTIC_INDEX.replace('-*', '')}-001`; + const index = `${DEFAULT_DIAGNOSTIC_INDEX_PATTERN.replace('-*', '')}-001`; await esClient.indices.create({ index, body: { settings: { hidden: true } } }); @@ -223,7 +223,7 @@ export async function dropEndpointIndices(esClient: ElasticsearchClient) { } export async function cleanupMockedEndpointAlerts(esClient: ElasticsearchClient) { - const index = `${DEFAULT_DIAGNOSTIC_INDEX.replace('-*', '')}-001`; + const index = `${DEFAULT_DIAGNOSTIC_INDEX_PATTERN.replace('-*', '')}-001`; await esClient.indices.delete({ index }).catch(() => { // ignore errors diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/register_routes.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/register_routes.ts index e6999cc9e429e..0cb3cac35b292 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/register_routes.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/register_routes.ts @@ -11,10 +11,6 @@ import type { SetupPlugins } from '../../../../plugin_contract'; import type { SecuritySolutionPluginRouter } from '../../../../types'; import { performBulkActionRoute } from './rules/bulk_actions/route'; -import { bulkCreateRulesRoute } from './rules/bulk_create_rules/route'; -import { bulkDeleteRulesRoute } from './rules/bulk_delete_rules/route'; -import { bulkPatchRulesRoute } from './rules/bulk_patch_rules/route'; -import { bulkUpdateRulesRoute } from './rules/bulk_update_rules/route'; import { createRuleRoute } from './rules/create_rule/route'; import { deleteRuleRoute } from './rules/delete_rule/route'; import { exportRulesRoute } from './rules/export_rules/route'; @@ -40,12 +36,6 @@ export const registerRuleManagementRoutes = ( patchRuleRoute(router); deleteRuleRoute(router); - // Rules bulk CRUD - bulkCreateRulesRoute(router, logger); - bulkUpdateRulesRoute(router, logger); - bulkPatchRulesRoute(router, logger); - bulkDeleteRulesRoute(router, logger); - // Rules bulk actions performBulkActionRoute(router, config, ml, logger); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.ts index 98910ea337630..9a3751bfb1d04 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.ts @@ -31,6 +31,8 @@ import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from '../. /** * @deprecated since version 8.2.0. Use the detection_engine/rules/_bulk_action API instead + * + * TODO: https://github.com/elastic/kibana/issues/193184 Delete this route and clean up the code */ export const bulkCreateRulesRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { router.versioned diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_delete_rules/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_delete_rules/route.ts index f582e19d2bcad..f24f563e1a99d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_delete_rules/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_delete_rules/route.ts @@ -45,6 +45,8 @@ type Handler = RequestHandler< /** * @deprecated since version 8.2.0. Use the detection_engine/rules/_bulk_action API instead + * + * TODO: https://github.com/elastic/kibana/issues/193184 Delete this route and clean up the code */ export const bulkDeleteRulesRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { const handler: Handler = async ( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.ts index da75e4e33362a..fc5edf0e65ac3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.ts @@ -25,6 +25,8 @@ import { RULE_MANAGEMENT_BULK_ACTION_SOCKET_TIMEOUT_MS } from '../../timeouts'; /** * @deprecated since version 8.2.0. Use the detection_engine/rules/_bulk_action API instead + * + * TODO: https://github.com/elastic/kibana/issues/193184 Delete this route and clean up the code */ export const bulkPatchRulesRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { router.versioned diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.ts index fb95e7e452afd..cccd1656d5091 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.ts @@ -29,6 +29,8 @@ import { RULE_MANAGEMENT_BULK_ACTION_SOCKET_TIMEOUT_MS } from '../../timeouts'; /** * @deprecated since version 8.2.0. Use the detection_engine/rules/_bulk_action API instead + * + * TODO: https://github.com/elastic/kibana/issues/193184 Delete this route and clean up the code */ export const bulkUpdateRulesRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { router.versioned diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/get_threshold_signal_history.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/get_threshold_signal_history.ts index 018d63c345e3a..e82e33c9e6e95 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/get_threshold_signal_history.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/get_threshold_signal_history.ts @@ -46,6 +46,9 @@ export const getThresholdSignalHistory = async ({ const response = await esClient.search({ ...request, index: indexPattern, + // If alerts index is not yet created, + // do not throw a 404 + ignore_unavailable: true, }); return { signalHistory: buildThresholdSignalHistory({ alerts: response.hits.hits }), diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/auditing/actions.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/auditing/actions.ts new file mode 100644 index 0000000000000..63d594a9711a3 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/auditing/actions.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 EntityEngineActions = { + INIT: 'init', + START: 'start', + STOP: 'stop', + CREATE: 'create', + DELETE: 'delete', + EXECUTE: 'execute', +} as const; + +export type EntityEngineActions = (typeof EntityEngineActions)[keyof typeof EntityEngineActions]; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/auditing/resources.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/auditing/resources.ts new file mode 100644 index 0000000000000..67d33fb42dc93 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/auditing/resources.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 EntityStoreResource = { + ENTITY_ENGINE: 'entity_engine', + ENTITY_DEFINITION: 'entity_definition', + ENTITY_INDEX: 'entity_index', + INDEX_COMPONENT_TEMPLATE: 'index_component_template', + PLATFORM_PIPELINE: 'platform_pipeline', + FIELD_RETENTION_ENRICH_POLICY: 'field_retention_enrich_policy', + FIELD_RETENTION_ENRICH_POLICY_TASK: 'field_retention_enrich_policy_task', +} as const; + +export type EntityStoreResource = (typeof EntityStoreResource)[keyof typeof EntityStoreResource]; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/constants.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/constants.ts index 796932d79b364..8b2e802b17b6d 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/constants.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/constants.ts @@ -9,8 +9,6 @@ import type { EngineStatus } from '../../../../common/api/entity_analytics'; export const DEFAULT_LOOKBACK_PERIOD = '24h'; -export const DEFAULT_INTERVAL = '30s'; - export const ENGINE_STATUS: Record, EngineStatus> = { INSTALLING: 'installing', STARTED: 'started', diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/elasticsearch_assets/ingest_pipeline.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/elasticsearch_assets/ingest_pipeline.ts index c2a5bff51f830..f4d2b848b726f 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/elasticsearch_assets/ingest_pipeline.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/elasticsearch_assets/ingest_pipeline.ts @@ -125,7 +125,7 @@ export const createPlatformPipeline = async ({ managed_by: 'entity_store', managed: true, }, - description: `Ingest pipeline for entity defiinition ${entityManagerDefinition.id}`, + description: `Ingest pipeline for entity definition ${entityManagerDefinition.id}`, processors: buildIngestPipeline({ namespace: unitedDefinition.namespace, version: unitedDefinition.version, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.test.ts index 858047952801d..733e85fd6ed55 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.test.ts @@ -15,6 +15,7 @@ import type { SortOrder } from '@elastic/elasticsearch/lib/api/types'; import type { EntityType } from '../../../../common/api/entity_analytics/entity_store/common.gen'; import type { DataViewsService } from '@kbn/data-views-plugin/common'; import type { AppClient } from '../../..'; +import type { EntityStoreConfig } from './types'; describe('EntityStoreDataClient', () => { const mockSavedObjectClient = savedObjectsClientMock.create(); @@ -29,6 +30,7 @@ describe('EntityStoreDataClient', () => { kibanaVersion: '9.0.0', dataViewsService: {} as DataViewsService, appClient: {} as AppClient, + config: {} as EntityStoreConfig, }); const defaultSearchParams = { diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts index 6b1d52661fa2c..d1d56aa0e08cb 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts @@ -11,6 +11,7 @@ import type { SavedObjectsClientContract, AuditLogger, IScopedClusterClient, + AuditEvent, AnalyticsServiceSetup, } from '@kbn/core/server'; import { EntityClient } from '@kbn/entityManager-plugin/server/lib/entity_client'; @@ -55,11 +56,14 @@ import { isPromiseFulfilled, isPromiseRejected, } from './utils'; +import { EntityEngineActions } from './auditing/actions'; +import { EntityStoreResource } from './auditing/resources'; +import { AUDIT_CATEGORY, AUDIT_OUTCOME, AUDIT_TYPE } from '../audit'; +import type { EntityRecord, EntityStoreConfig } from './types'; import { ENTITY_ENGINE_INITIALIZATION_EVENT, ENTITY_ENGINE_RESOURCE_INIT_FAILURE_EVENT, } from '../../telemetry/event_based/events'; -import type { EntityRecord } from './types'; import { CRITICALITY_VALUES } from '../asset_criticality/constants'; interface EntityStoreClientOpts { @@ -72,6 +76,7 @@ interface EntityStoreClientOpts { kibanaVersion: string; dataViewsService: DataViewsService; appClient: AppClient; + config: EntityStoreConfig; telemetry?: AnalyticsServiceSetup; } @@ -130,7 +135,7 @@ export class EntityStoreDataClient { throw new Error('Task Manager is not available'); } - const { logger } = this.options; + const { config } = this.options; await this.riskScoreDataClient.createRiskScoreLatestIndex(); @@ -142,8 +147,13 @@ export class EntityStoreDataClient { 'Asset criticality data migration is required before initializing entity store. If this error persists, please restart Kibana.' ); } - logger.info( - `In namespace ${this.options.namespace}: Initializing entity store for ${entityType}` + + this.log('info', entityType, `Initializing entity store`); + this.audit( + EntityEngineActions.INIT, + EntityStoreResource.ENTITY_ENGINE, + entityType, + 'Initializing entity engine' ); const descriptor = await this.engineClient.init(entityType, { @@ -151,9 +161,7 @@ export class EntityStoreDataClient { fieldHistoryLength, indexPattern, }); - logger.debug(`Initialized engine for ${entityType}`); - // first create the entity definition without starting it - // so that the index template is created which we can add a component template to + this.log('debug', entityType, `Initialized engine saved object`); this.asyncSetup( entityType, @@ -161,10 +169,11 @@ export class EntityStoreDataClient { this.options.taskManager, indexPattern, filter, + config, pipelineDebugMode - ).catch((error) => { - logger.error(`There was an error during async setup of the Entity Store: ${error}`); - }); + ).catch((e) => + this.log('error', entityType, `Error during async setup of entity store: ${e.message}`) + ); return descriptor; } @@ -175,6 +184,7 @@ export class EntityStoreDataClient { taskManager: TaskManagerStartContract, indexPattern: string, filter: string, + config: EntityStoreConfig, pipelineDebugMode: boolean ) { const setupStartTime = moment().utc().toISOString(); @@ -186,12 +196,11 @@ export class EntityStoreDataClient { entityType, namespace, fieldHistoryLength, + syncDelay: `${config.syncDelay.asSeconds()}s`, + frequency: `${config.frequency.asSeconds()}s`, }); const { entityManagerDefinition } = unitedDefinition; - const debugLog = (message: string) => - logger.debug(`[Entity Engine] [${entityType}] ${message}`); - try { // clean up any existing entity store await this.delete(entityType, taskManager, { deleteData: false, deleteEngine: false }); @@ -207,7 +216,7 @@ export class EntityStoreDataClient { }, installOnly: true, }); - debugLog(`Created entity definition`); + this.log(`debug`, entityType, `Created entity definition`); // the index must be in place with the correct mapping before the enrich policy is created // this is because the enrich policy will fail if the index does not exist with the correct fields @@ -215,14 +224,14 @@ export class EntityStoreDataClient { unitedDefinition, esClient: this.esClient, }); - debugLog(`Created entity index component template`); + this.log(`debug`, entityType, `Created entity index component template`); await createEntityIndex({ entityType, esClient: this.esClient, namespace, logger, }); - debugLog(`Created entity index`); + this.log(`debug`, entityType, `Created entity index`); // we must create and execute the enrich policy before the pipeline is created // this is because the pipeline will fail if the enrich index does not exist @@ -230,24 +239,24 @@ export class EntityStoreDataClient { unitedDefinition, esClient: this.esClient, }); - debugLog(`Created field retention enrich policy`); + this.log(`debug`, entityType, `Created field retention enrich policy`); + await executeFieldRetentionEnrichPolicy({ unitedDefinition, esClient: this.esClient, logger, }); - debugLog(`Executed field retention enrich policy`); + this.log(`debug`, entityType, `Executed field retention enrich policy`); await createPlatformPipeline({ debugMode: pipelineDebugMode, unitedDefinition, logger, esClient: this.esClient, }); - debugLog(`Created @platform pipeline`); + this.log(`debug`, entityType, `Created @platform pipeline`); // finally start the entity definition now that everything is in place const updated = await this.start(entityType, { force: true }); - debugLog(`Started entity definition`); // the task will execute the enrich policy on a schedule await startEntityStoreFieldRetentionEnrichTask({ @@ -255,7 +264,9 @@ export class EntityStoreDataClient { logger, taskManager, }); - logger.info(`Entity store initialized for ${entityType}`); + + this.log(`debug`, entityType, `Started entity store field retention enrich task`); + this.log(`info`, entityType, `Entity store initialized`); const setupEndTime = moment().utc().toISOString(); const duration = moment(setupEndTime).diff(moment(setupStartTime), 'seconds'); @@ -265,15 +276,28 @@ export class EntityStoreDataClient { return updated; } catch (err) { - this.options.logger.error( - `Error initializing entity store for ${entityType}: ${err.message}` + this.log(`error`, entityType, `Error initializing entity store: ${err.message}`); + + this.audit( + EntityEngineActions.INIT, + EntityStoreResource.ENTITY_ENGINE, + entityType, + 'Failed to initialize entity engine resources', + err ); this.options.telemetry?.reportEvent(ENTITY_ENGINE_RESOURCE_INIT_FAILURE_EVENT.eventType, { error: err.message, }); - await this.engineClient.update(entityType, ENGINE_STATUS.ERROR); + await this.engineClient.update(entityType, { + status: ENGINE_STATUS.ERROR, + error: { + message: err.message, + stack: err.stack, + action: 'init', + }, + }); await this.delete(entityType, taskManager, { deleteData: true, deleteEngine: false }); } @@ -296,43 +320,56 @@ export class EntityStoreDataClient { } public async start(entityType: EntityType, options?: { force: boolean }) { + const { namespace } = this.options; const descriptor = await this.engineClient.get(entityType); if (!options?.force && descriptor.status !== ENGINE_STATUS.STOPPED) { throw new Error( - `In namespace ${this.options.namespace}: Cannot start Entity engine for ${entityType} when current status is: ${descriptor.status}` + `In namespace ${namespace}: Cannot start Entity engine for ${entityType} when current status is: ${descriptor.status}` ); } - this.options.logger.info( - `In namespace ${this.options.namespace}: Starting entity store for ${entityType}` - ); + this.log('info', entityType, `Starting entity store`); // startEntityDefinition requires more fields than the engine descriptor // provides so we need to fetch the full entity definition const fullEntityDefinition = await this.getExistingEntityDefinition(entityType); + this.audit( + EntityEngineActions.START, + EntityStoreResource.ENTITY_DEFINITION, + entityType, + 'Starting entity definition' + ); await this.entityClient.startEntityDefinition(fullEntityDefinition); + this.log('debug', entityType, `Started entity definition`); - return this.engineClient.update(entityType, ENGINE_STATUS.STARTED); + return this.engineClient.updateStatus(entityType, ENGINE_STATUS.STARTED); } public async stop(entityType: EntityType) { + const { namespace } = this.options; const descriptor = await this.engineClient.get(entityType); if (descriptor.status !== ENGINE_STATUS.STARTED) { throw new Error( - `In namespace ${this.options.namespace}: Cannot stop Entity engine for ${entityType} when current status is: ${descriptor.status}` + `In namespace ${namespace}: Cannot stop Entity engine for ${entityType} when current status is: ${descriptor.status}` ); } - this.options.logger.info( - `In namespace ${this.options.namespace}: Stopping entity store for ${entityType}` - ); + this.log('info', entityType, `Stopping entity store`); + // stopEntityDefinition requires more fields than the engine descriptor // provides so we need to fetch the full entity definition const fullEntityDefinition = await this.getExistingEntityDefinition(entityType); + this.audit( + EntityEngineActions.STOP, + EntityStoreResource.ENTITY_DEFINITION, + entityType, + 'Stopping entity definition' + ); await this.entityClient.stopEntityDefinition(fullEntityDefinition); + this.log('debug', entityType, `Stopped entity definition`); - return this.engineClient.update(entityType, ENGINE_STATUS.STOPPED); + return this.engineClient.updateStatus(entityType, ENGINE_STATUS.STOPPED); } public async get(entityType: EntityType) { @@ -348,42 +385,62 @@ export class EntityStoreDataClient { taskManager: TaskManagerStartContract, options = { deleteData: false, deleteEngine: true } ) { - const { namespace, logger, appClient, dataViewsService } = this.options; + const { namespace, logger, appClient, dataViewsService, config } = this.options; const { deleteData, deleteEngine } = options; const descriptor = await this.engineClient.maybeGet(entityType); const indexPatterns = await buildIndexPatterns(namespace, appClient, dataViewsService); + + // TODO delete unitedDefinition from this method. we only need the id for deletion const unitedDefinition = getUnitedEntityDefinition({ indexPatterns, entityType, namespace: this.options.namespace, fieldHistoryLength: descriptor?.fieldHistoryLength ?? 10, + syncDelay: `${config.syncDelay.asSeconds()}s`, + frequency: `${config.frequency.asSeconds()}s`, }); const { entityManagerDefinition } = unitedDefinition; - logger.info(`In namespace ${namespace}: Deleting entity store for ${entityType}`); + + this.log('info', entityType, `Deleting entity store`); + this.audit( + EntityEngineActions.DELETE, + EntityStoreResource.ENTITY_ENGINE, + entityType, + 'Deleting entity engine' + ); + try { - try { - await this.entityClient.deleteEntityDefinition({ + await this.entityClient + .deleteEntityDefinition({ id: entityManagerDefinition.id, deleteData, - }); - } catch (e) { - logger.error(`Error deleting entity definition for ${entityType}: ${e.message}`); - } + }) + // Swallowing the error as it is expected to fail if no entity definition exists + .catch((e) => + this.log(`warn`, entityType, `Error deleting entity definition: ${e.message}`) + ); + this.log('debug', entityType, `Deleted entity definition`); + await deleteEntityIndexComponentTemplate({ unitedDefinition, esClient: this.esClient, }); + this.log('debug', entityType, `Deleted entity index component template`); + await deletePlatformPipeline({ unitedDefinition, logger, esClient: this.esClient, }); + this.log('debug', entityType, `Deleted platform pipeline`); + await deleteFieldRetentionEnrichPolicy({ unitedDefinition, esClient: this.esClient, logger, }); + this.log('debug', entityType, `Deleted field retention enrich policy`); if (deleteData) { await deleteEntityIndex({ @@ -392,6 +449,7 @@ export class EntityStoreDataClient { namespace, logger, }); + this.log('debug', entityType, `Deleted entity index`); } if (descriptor && deleteEngine) { @@ -405,13 +463,23 @@ export class EntityStoreDataClient { logger, taskManager, }); + this.log('debug', entityType, `Deleted entity store field retention enrich task`); } + logger.info(`[Entity Store] In namespace ${namespace}: Deleted store for ${entityType}`); return { deleted: true }; - } catch (e) { - logger.error(`Error deleting entity store for ${entityType}: ${e.message}`); - // TODO: should we set the engine status to error here? - throw e; + } catch (err) { + this.log(`error`, entityType, `Error deleting entity store: ${err.message}`); + + this.audit( + EntityEngineActions.DELETE, + EntityStoreResource.ENTITY_ENGINE, + entityType, + 'Failed to delete entity engine', + err + ); + + throw err; } } @@ -499,7 +567,7 @@ export class EntityStoreDataClient { } // Update savedObject status - await this.engineClient.update(engine.type, ENGINE_STATUS.UPDATING); + await this.engineClient.updateStatus(engine.type, ENGINE_STATUS.UPDATING); try { // Update entity manager definition @@ -512,12 +580,12 @@ export class EntityStoreDataClient { }); // Restore the savedObject status and set the new index pattern - await this.engineClient.update(engine.type, originalStatus); + await this.engineClient.updateStatus(engine.type, originalStatus); return { type: engine.type, changes: { indexPatterns } }; } catch (error) { // Rollback the engine initial status when the update fails - await this.engineClient.update(engine.type, originalStatus); + await this.engineClient.updateStatus(engine.type, originalStatus); throw error; } @@ -539,4 +607,48 @@ export class EntityStoreDataClient { errors: updateErrors, }; } + + private log( + level: Exclude, + entityType: EntityType, + msg: string + ) { + this.options.logger[level]( + `[Entity Engine] [entity.${entityType}] [namespace: ${this.options.namespace}] ${msg}` + ); + } + + private audit( + action: EntityEngineActions, + resource: EntityStoreResource, + entityType: EntityType, + msg: string, + error?: Error + ) { + // NOTE: Excluding errors, all auditing events are currently WRITE events, meaning the outcome is always UNKNOWN. + // This may change in the future, depending on the audit action. + const outcome = error ? AUDIT_OUTCOME.FAILURE : AUDIT_OUTCOME.UNKNOWN; + + const type = + action === EntityEngineActions.CREATE + ? AUDIT_TYPE.CREATION + : EntityEngineActions.DELETE + ? AUDIT_TYPE.DELETION + : AUDIT_TYPE.CHANGE; + + const category = AUDIT_CATEGORY.DATABASE; + + const message = error ? `${msg}: ${error.message}` : msg; + const event: AuditEvent = { + message: `[Entity Engine] [entity.${entityType}] ${message}`, + event: { + action: `${action}_${entityType}_${resource}`, + category, + outcome, + type, + }, + }; + + return this.options.auditLogger?.log(event); + } } diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/stop.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/stop.ts index e1c28bc2cc073..3ec84e13aa1db 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/stop.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/stop.ts @@ -47,7 +47,7 @@ export const stopEntityEngineRoute = ( return response.ok({ body: { stopped: engine.status === ENGINE_STATUS.STOPPED } }); } catch (e) { - logger.error('Error in StopEntityEngine:', e); + logger.error(`Error in StopEntityEngine: ${e.message}`); const error = transformError(e); return siemResponse.error({ statusCode: error.statusCode, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/saved_object/engine_descriptor.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/saved_object/engine_descriptor.ts index af7b4ba80dde5..cfaea1b1da0ff 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/saved_object/engine_descriptor.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/saved_object/engine_descriptor.ts @@ -78,17 +78,21 @@ export class EngineDescriptorClient { return attributes; } - async update(entityType: EntityType, status: EngineStatus) { + async update(entityType: EntityType, engine: Partial) { const id = this.getSavedObjectId(entityType); const { attributes } = await this.deps.soClient.update( entityEngineDescriptorTypeName, id, - { status }, + engine, { refresh: 'wait_for' } ); return attributes; } + async updateStatus(entityType: EntityType, status: EngineStatus) { + return this.update(entityType, { status }); + } + async find(entityType: EntityType): Promise> { return this.deps.soClient.find({ type: entityEngineDescriptorTypeName, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/task/field_retention_enrichment_task.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/task/field_retention_enrichment_task.ts index 5227473e0e51b..708b74277faae 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/task/field_retention_enrichment_task.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/task/field_retention_enrichment_task.ts @@ -36,12 +36,12 @@ import { const logFactory = (logger: Logger, taskId: string) => (message: string): void => - logger.info(`[task ${taskId}]: ${message}`); + logger.info(`[Entity Store] [task ${taskId}]: ${message}`); const debugLogFactory = (logger: Logger, taskId: string) => (message: string): void => - logger.debug(`[task ${taskId}]: ${message}`); + logger.debug(`[Entity Store] [task ${taskId}]: ${message}`); const getTaskName = (): string => TYPE; @@ -65,7 +65,9 @@ export const registerEntityStoreFieldRetentionEnrichTask = ({ taskManager: TaskManagerSetupContract | undefined; }): void => { if (!taskManager) { - logger.info('Task Manager is unavailable; skipping entity store enrich policy registration.'); + logger.info( + '[Entity Store] Task Manager is unavailable; skipping entity store enrich policy registration.' + ); return; } @@ -134,7 +136,7 @@ export const startEntityStoreFieldRetentionEnrichTask = async ({ params: { version: VERSION }, }); } catch (e) { - logger.warn(`[task ${taskId}]: error scheduling task, received ${e.message}`); + logger.warn(`[Entity Store] [task ${taskId}]: error scheduling task, received ${e.message}`); throw e; } }; @@ -150,9 +152,14 @@ export const removeEntityStoreFieldRetentionEnrichTask = async ({ }) => { try { await taskManager.remove(getTaskId(namespace)); + logger.info( + `[Entity Store] Removed entity store enrich policy task for namespace ${namespace}` + ); } catch (err) { if (!SavedObjectsErrorHelpers.isNotFoundError(err)) { - logger.error(`Failed to remove entity store enrich policy task: ${err.message}`); + logger.error( + `[Entity Store] Failed to remove entity store enrich policy task: ${err.message}` + ); throw err; } } @@ -233,7 +240,7 @@ export const runTask = async ({ state: updatedState, }; } catch (e) { - logger.error(`[task ${taskId}]: error running task, received ${e.message}`); + logger.error(`[Entity Store] [task ${taskId}]: error running task, received ${e.message}`); throw e; } }; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/types.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/types.ts index e5f1e6db36bca..b71380b2e0677 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/types.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/types.ts @@ -7,6 +7,7 @@ import type { HostEntity, UserEntity } from '../../../../common/api/entity_analytics'; import type { CriticalityValues } from '../asset_criticality/constants'; +import type { EntityAnalyticsConfig } from '../types'; export interface HostEntityRecord extends Omit { asset?: { @@ -24,3 +25,5 @@ export interface UserEntityRecord extends Omit { * It represents the data stored in the entity store index. */ export type EntityRecord = HostEntityRecord | UserEntityRecord; + +export type EntityStoreConfig = EntityAnalyticsConfig['entityStore']; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.test.ts index d9c54e1fcd288..fa443ffa94047 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.test.ts @@ -15,6 +15,8 @@ describe('getUnitedEntityDefinition', () => { namespace: 'test', fieldHistoryLength: 10, indexPatterns, + syncDelay: '1m', + frequency: '1m', }); it('mapping', () => { @@ -172,6 +174,10 @@ describe('getUnitedEntityDefinition', () => { ], "latest": Object { "lookbackPeriod": "24h", + "settings": Object { + "frequency": "1m", + "syncDelay": "1m", + }, "timestampField": "@timestamp", }, "managed": true, @@ -312,6 +318,8 @@ describe('getUnitedEntityDefinition', () => { namespace: 'test', fieldHistoryLength: 10, indexPatterns, + syncDelay: '1m', + frequency: '1m', }); it('mapping', () => { @@ -445,6 +453,10 @@ describe('getUnitedEntityDefinition', () => { ], "latest": Object { "lookbackPeriod": "24h", + "settings": Object { + "frequency": "1m", + "syncDelay": "1m", + }, "timestampField": "@timestamp", }, "managed": true, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.ts index 32cb52a61d469..ba4963d5fea0a 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.ts @@ -25,6 +25,8 @@ interface Options { namespace: string; fieldHistoryLength: number; indexPatterns: string[]; + syncDelay: string; + frequency: string; } export const getUnitedEntityDefinition = memoize( @@ -33,6 +35,8 @@ export const getUnitedEntityDefinition = memoize( namespace, fieldHistoryLength, indexPatterns, + syncDelay, + frequency, }: Options): UnitedEntityDefinition => { const unitedDefinition = unitedDefinitionBuilders[entityType](fieldHistoryLength); @@ -47,6 +51,8 @@ export const getUnitedEntityDefinition = memoize( ...unitedDefinition, namespace, indexPatterns, + syncDelay, + frequency, }); } ); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/united_entity_definition.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/united_entity_definition.ts index c5315c5dca2b0..eced765c75193 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/united_entity_definition.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/united_entity_definition.ts @@ -7,7 +7,7 @@ import { entityDefinitionSchema, type EntityDefinition } from '@kbn/entities-schema'; import type { MappingTypeMapping } from '@elastic/elasticsearch/lib/api/types'; import type { EntityType } from '../../../../../common/api/entity_analytics/entity_store/common.gen'; -import { DEFAULT_INTERVAL, DEFAULT_LOOKBACK_PERIOD } from '../constants'; +import { DEFAULT_LOOKBACK_PERIOD } from '../constants'; import { buildEntityDefinitionId, getIdentityFieldForEntityType } from '../utils'; import type { FieldRetentionDefinition, @@ -25,6 +25,8 @@ export class UnitedEntityDefinition { entityManagerDefinition: EntityDefinition; fieldRetentionDefinition: FieldRetentionDefinition; indexMappings: MappingTypeMapping; + syncDelay: string; + frequency: string; constructor(opts: { version: string; @@ -32,11 +34,15 @@ export class UnitedEntityDefinition { indexPatterns: string[]; fields: UnitedDefinitionField[]; namespace: string; + syncDelay: string; + frequency: string; }) { this.version = opts.version; this.entityType = opts.entityType; this.indexPatterns = opts.indexPatterns; this.fields = opts.fields; + this.frequency = opts.frequency; + this.syncDelay = opts.syncDelay; this.namespace = opts.namespace; this.entityManagerDefinition = this.toEntityManagerDefinition(); this.fieldRetentionDefinition = this.toFieldRetentionDefinition(); @@ -44,7 +50,7 @@ export class UnitedEntityDefinition { } private toEntityManagerDefinition(): EntityDefinition { - const { entityType, namespace, indexPatterns } = this; + const { entityType, namespace, indexPatterns, syncDelay, frequency } = this; const identityField = getIdentityFieldForEntityType(this.entityType); const metadata = this.fields .filter((field) => field.definition) @@ -61,7 +67,10 @@ export class UnitedEntityDefinition { latest: { timestampField: '@timestamp', lookbackPeriod: DEFAULT_LOOKBACK_PERIOD, - interval: DEFAULT_INTERVAL, + settings: { + syncDelay, + frequency, + }, }, version: this.version, managed: true, diff --git a/x-pack/plugins/security_solution/server/lib/product_features_service/product_features_service.test.ts b/x-pack/plugins/security_solution/server/lib/product_features_service/product_features_service.test.ts index a1b71a9c4f04f..8d274a30ca3c9 100644 --- a/x-pack/plugins/security_solution/server/lib/product_features_service/product_features_service.test.ts +++ b/x-pack/plugins/security_solution/server/lib/product_features_service/product_features_service.test.ts @@ -23,6 +23,7 @@ import type { import { ProductFeatureKey } from '@kbn/security-solution-features/keys'; import { httpServiceMock } from '@kbn/core-http-server-mocks'; import type { + AuthzEnabled, KibanaRequest, LifecycleResponseFactory, OnPostAuthHandler, @@ -181,11 +182,6 @@ describe('ProductFeaturesService', () => { lastRegisteredFn = fn; }); - const getReq = (tags: string[] = []) => - ({ - route: { options: { tags } }, - url: { pathname: '', search: '' }, - } as unknown as KibanaRequest); const res = { notFound: jest.fn() } as unknown as LifecycleResponseFactory; const toolkit = httpServiceMock.createOnPostAuthToolkit(); @@ -204,93 +200,281 @@ describe('ProductFeaturesService', () => { expect(mockHttpSetup.registerOnPostAuth).toHaveBeenCalledTimes(1); }); - it('should authorize when no tag matches', async () => { - const experimentalFeatures = {} as ExperimentalFeatures; - const productFeaturesService = new ProductFeaturesService( - loggerMock.create(), - experimentalFeatures - ); - productFeaturesService.registerApiAccessControl(mockHttpSetup); - - await lastRegisteredFn(getReq(['access:something', 'access:securitySolution']), res, toolkit); - - expect(MockedProductFeatures.mock.instances[0].isActionRegistered).not.toHaveBeenCalled(); - expect(res.notFound).not.toHaveBeenCalled(); - expect(toolkit.next).toHaveBeenCalledTimes(1); - }); - - it('should check when tag matches and return not found when not action registered', async () => { - const experimentalFeatures = {} as ExperimentalFeatures; - const productFeaturesService = new ProductFeaturesService( - loggerMock.create(), - experimentalFeatures - ); - productFeaturesService.registerApiAccessControl(mockHttpSetup); - - (MockedProductFeatures.mock.instances[0].isActionRegistered as jest.Mock).mockReturnValueOnce( - false - ); - await lastRegisteredFn(getReq(['access:securitySolution-foo']), res, toolkit); - - expect(MockedProductFeatures.mock.instances[0].isActionRegistered).toHaveBeenCalledWith( - 'api:securitySolution-foo' - ); - expect(res.notFound).toHaveBeenCalledTimes(1); - expect(toolkit.next).not.toHaveBeenCalled(); - }); - - it('should check when tag matches and continue when action registered', async () => { - const experimentalFeatures = {} as ExperimentalFeatures; - const productFeaturesService = new ProductFeaturesService( - loggerMock.create(), - experimentalFeatures - ); - productFeaturesService.registerApiAccessControl(mockHttpSetup); - - (MockedProductFeatures.mock.instances[0].isActionRegistered as jest.Mock).mockReturnValueOnce( - true - ); - await lastRegisteredFn(getReq(['access:securitySolution-foo']), res, toolkit); - - expect(MockedProductFeatures.mock.instances[0].isActionRegistered).toHaveBeenCalledWith( - 'api:securitySolution-foo' - ); - expect(res.notFound).not.toHaveBeenCalled(); - expect(toolkit.next).toHaveBeenCalledTimes(1); - }); - - it('should check when productFeature tag when it matches and return not found when not enabled', async () => { - const experimentalFeatures = {} as ExperimentalFeatures; - const productFeaturesService = new ProductFeaturesService( - loggerMock.create(), - experimentalFeatures - ); - productFeaturesService.registerApiAccessControl(mockHttpSetup); - - productFeaturesService.isEnabled = jest.fn().mockReturnValueOnce(false); - - await lastRegisteredFn(getReq(['securitySolutionProductFeature:foo']), res, toolkit); - - expect(productFeaturesService.isEnabled).toHaveBeenCalledWith('foo'); - expect(res.notFound).toHaveBeenCalledTimes(1); - expect(toolkit.next).not.toHaveBeenCalled(); + describe('when using productFeature tag', () => { + const getReq = (tags: string[] = []) => + ({ + route: { options: { tags } }, + url: { pathname: '', search: '' }, + } as unknown as KibanaRequest); + + it('should check when productFeature tag when it matches and return not found when not enabled', async () => { + const experimentalFeatures = {} as ExperimentalFeatures; + const productFeaturesService = new ProductFeaturesService( + loggerMock.create(), + experimentalFeatures + ); + productFeaturesService.registerApiAccessControl(mockHttpSetup); + + productFeaturesService.isEnabled = jest.fn().mockReturnValueOnce(false); + + await lastRegisteredFn(getReq(['securitySolutionProductFeature:foo']), res, toolkit); + + expect(productFeaturesService.isEnabled).toHaveBeenCalledWith('foo'); + expect(res.notFound).toHaveBeenCalledTimes(1); + expect(toolkit.next).not.toHaveBeenCalled(); + }); + + it('should check when productFeature tag when it matches and continue when enabled', async () => { + const experimentalFeatures = {} as ExperimentalFeatures; + const productFeaturesService = new ProductFeaturesService( + loggerMock.create(), + experimentalFeatures + ); + productFeaturesService.registerApiAccessControl(mockHttpSetup); + + productFeaturesService.isEnabled = jest.fn().mockReturnValueOnce(true); + + await lastRegisteredFn(getReq(['securitySolutionProductFeature:foo']), res, toolkit); + + expect(productFeaturesService.isEnabled).toHaveBeenCalledWith('foo'); + expect(res.notFound).not.toHaveBeenCalled(); + expect(toolkit.next).toHaveBeenCalledTimes(1); + }); }); - it('should check when productFeature tag when it matches and continue when enabled', async () => { - const experimentalFeatures = {} as ExperimentalFeatures; - const productFeaturesService = new ProductFeaturesService( - loggerMock.create(), - experimentalFeatures - ); - productFeaturesService.registerApiAccessControl(mockHttpSetup); - - productFeaturesService.isEnabled = jest.fn().mockReturnValueOnce(true); - - await lastRegisteredFn(getReq(['securitySolutionProductFeature:foo']), res, toolkit); - - expect(productFeaturesService.isEnabled).toHaveBeenCalledWith('foo'); - expect(res.notFound).not.toHaveBeenCalled(); - expect(toolkit.next).toHaveBeenCalledTimes(1); + // Documentation: https://docs.elastic.dev/kibana-dev-docs/key-concepts/security-api-authorization + describe('when using authorization', () => { + let productFeaturesService: ProductFeaturesService; + let mockIsActionRegistered: jest.Mock; + + beforeEach(() => { + const experimentalFeatures = {} as ExperimentalFeatures; + productFeaturesService = new ProductFeaturesService( + loggerMock.create(), + experimentalFeatures + ); + productFeaturesService.registerApiAccessControl(mockHttpSetup); + mockIsActionRegistered = MockedProductFeatures.mock.instances[0] + .isActionRegistered as jest.Mock; + }); + + describe('when using access tag', () => { + const getReq = (tags: string[] = []) => + ({ + route: { options: { tags } }, + url: { pathname: '', search: '' }, + } as unknown as KibanaRequest); + + it('should authorize when no tag matches', async () => { + await lastRegisteredFn( + getReq(['access:something', 'access:securitySolution']), + res, + toolkit + ); + + expect(mockIsActionRegistered).not.toHaveBeenCalled(); + expect(res.notFound).not.toHaveBeenCalled(); + expect(toolkit.next).toHaveBeenCalledTimes(1); + }); + + it('should check when tag matches and return not found when not action registered', async () => { + mockIsActionRegistered.mockReturnValueOnce(false); + await lastRegisteredFn(getReq(['access:securitySolution-foo']), res, toolkit); + + expect(mockIsActionRegistered).toHaveBeenCalledWith('api:securitySolution-foo'); + expect(res.notFound).toHaveBeenCalledTimes(1); + expect(toolkit.next).not.toHaveBeenCalled(); + }); + + it('should check when tag matches and continue when action registered', async () => { + mockIsActionRegistered.mockReturnValueOnce(true); + await lastRegisteredFn(getReq(['access:securitySolution-foo']), res, toolkit); + + expect(mockIsActionRegistered).toHaveBeenCalledWith('api:securitySolution-foo'); + expect(res.notFound).not.toHaveBeenCalled(); + expect(toolkit.next).toHaveBeenCalledTimes(1); + }); + }); + + describe('when using security authz', () => { + beforeEach(() => { + mockIsActionRegistered.mockImplementation((action: string) => action.includes('enabled')); + }); + + const getReq = (requiredPrivileges?: AuthzEnabled['requiredPrivileges']) => + ({ + route: { options: { security: { authz: { requiredPrivileges } } } }, + url: { pathname: '', search: '' }, + } as unknown as KibanaRequest); + + it('should authorize when no privilege matches', async () => { + await lastRegisteredFn(getReq(['something', 'securitySolution']), res, toolkit); + + expect(mockIsActionRegistered).not.toHaveBeenCalled(); + expect(res.notFound).not.toHaveBeenCalled(); + expect(toolkit.next).toHaveBeenCalledTimes(1); + }); + + it('should check when privilege matches and return not found when not action registered', async () => { + await lastRegisteredFn(getReq(['securitySolution-disabled']), res, toolkit); + + expect(mockIsActionRegistered).toHaveBeenCalledWith('api:securitySolution-disabled'); + expect(res.notFound).toHaveBeenCalledTimes(1); + expect(toolkit.next).not.toHaveBeenCalled(); + }); + + it('should check when privilege matches and continue when action registered', async () => { + mockIsActionRegistered.mockReturnValueOnce(true); + await lastRegisteredFn(getReq(['securitySolution-enabled']), res, toolkit); + + expect(mockIsActionRegistered).toHaveBeenCalledWith('api:securitySolution-enabled'); + expect(res.notFound).not.toHaveBeenCalled(); + expect(toolkit.next).toHaveBeenCalledTimes(1); + }); + + it('should restrict access when one action is not registered', async () => { + mockIsActionRegistered.mockReturnValueOnce(true); + await lastRegisteredFn( + getReq([ + 'securitySolution-enabled', + 'securitySolution-disabled', + 'securitySolution-enabled2', + ]), + res, + toolkit + ); + + expect(mockIsActionRegistered).toHaveBeenCalledTimes(2); + expect(mockIsActionRegistered).toHaveBeenCalledWith('api:securitySolution-enabled'); + expect(mockIsActionRegistered).toHaveBeenCalledWith('api:securitySolution-disabled'); + + expect(res.notFound).toHaveBeenCalledTimes(1); + expect(toolkit.next).not.toHaveBeenCalled(); + }); + + describe('when using nested requiredPrivileges', () => { + describe('when using allRequired', () => { + it('should allow access when all actions are registered', async () => { + const req = getReq([ + { + allRequired: [ + 'securitySolution-enabled', + 'securitySolution-enabled2', + 'securitySolution-enabled3', + ], + }, + ]); + await lastRegisteredFn(req, res, toolkit); + + expect(mockIsActionRegistered).toHaveBeenCalledTimes(3); + expect(mockIsActionRegistered).toHaveBeenCalledWith('api:securitySolution-enabled'); + expect(mockIsActionRegistered).toHaveBeenCalledWith('api:securitySolution-enabled2'); + expect(mockIsActionRegistered).toHaveBeenCalledWith('api:securitySolution-enabled3'); + + expect(res.notFound).not.toHaveBeenCalled(); + expect(toolkit.next).toHaveBeenCalledTimes(1); + }); + + it('should restrict access if one action is not registered', async () => { + const req = getReq([ + { + allRequired: [ + 'securitySolution-enabled', + 'securitySolution-disabled', + 'securitySolution-notCalled', + ], + }, + ]); + await lastRegisteredFn(req, res, toolkit); + + expect(mockIsActionRegistered).toHaveBeenCalledTimes(2); + expect(mockIsActionRegistered).toHaveBeenCalledWith('api:securitySolution-enabled'); + expect(mockIsActionRegistered).toHaveBeenCalledWith('api:securitySolution-disabled'); + + expect(res.notFound).toHaveBeenCalledTimes(1); + expect(toolkit.next).not.toHaveBeenCalled(); + }); + + it('should allow only based on security privileges and ignore non-security', async () => { + const req = getReq([ + { allRequired: ['notSecurityPrivilege', 'securitySolution-enabled'] }, + ]); + await lastRegisteredFn(req, res, toolkit); + + expect(mockIsActionRegistered).toHaveBeenCalledTimes(1); + expect(mockIsActionRegistered).toHaveBeenCalledWith('api:securitySolution-enabled'); + + expect(res.notFound).not.toHaveBeenCalled(); + expect(toolkit.next).toHaveBeenCalledTimes(1); + }); + + it('should restrict only based on security privileges and ignore non-security', async () => { + const req = getReq([ + { allRequired: ['notSecurityPrivilege', 'securitySolution-disabled'] }, + ]); + await lastRegisteredFn(req, res, toolkit); + + expect(mockIsActionRegistered).toHaveBeenCalledTimes(1); + expect(mockIsActionRegistered).toHaveBeenCalledWith('api:securitySolution-disabled'); + + expect(res.notFound).toHaveBeenCalledTimes(1); + expect(toolkit.next).not.toHaveBeenCalled(); + }); + }); + + describe('when using anyRequired', () => { + it('should allow access when one action is registered', async () => { + const req = getReq([ + { + anyRequired: [ + 'securitySolution-disabled', + 'securitySolution-enabled', + 'securitySolution-notCalled', + ], + }, + ]); + await lastRegisteredFn(req, res, toolkit); + + expect(mockIsActionRegistered).toHaveBeenCalledTimes(2); + expect(mockIsActionRegistered).toHaveBeenCalledWith('api:securitySolution-disabled'); + expect(mockIsActionRegistered).toHaveBeenCalledWith('api:securitySolution-enabled'); + + expect(res.notFound).not.toHaveBeenCalled(); + expect(toolkit.next).toHaveBeenCalledTimes(1); + }); + + it('should restrict access when no action is registered', async () => { + const req = getReq([ + { + anyRequired: ['securitySolution-disabled', 'securitySolution-disabled2'], + }, + ]); + await lastRegisteredFn(req, res, toolkit); + + expect(mockIsActionRegistered).toHaveBeenCalledTimes(2); + expect(mockIsActionRegistered).toHaveBeenCalledWith('api:securitySolution-disabled'); + expect(mockIsActionRegistered).toHaveBeenCalledWith('api:securitySolution-disabled2'); + + expect(res.notFound).toHaveBeenCalledTimes(1); + expect(toolkit.next).not.toHaveBeenCalled(); + }); + + it('should restrict only based on security privileges and allow when non-security privilege is present', async () => { + const req = getReq([ + { + anyRequired: ['notSecurityPrivilege', 'securitySolution-disabled'], + }, + ]); + await lastRegisteredFn(req, res, toolkit); + + expect(mockIsActionRegistered).not.toHaveBeenCalled(); + + expect(res.notFound).not.toHaveBeenCalled(); + expect(toolkit.next).toHaveBeenCalledTimes(1); + }); + }); + }); + }); }); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/product_features_service/product_features_service.ts b/x-pack/plugins/security_solution/server/lib/product_features_service/product_features_service.ts index 29ef513b40bb3..86928ff905545 100644 --- a/x-pack/plugins/security_solution/server/lib/product_features_service/product_features_service.ts +++ b/x-pack/plugins/security_solution/server/lib/product_features_service/product_features_service.ts @@ -11,7 +11,7 @@ * 2.0. */ -import type { HttpServiceSetup, Logger } from '@kbn/core/server'; +import type { AuthzEnabled, HttpServiceSetup, Logger, RouteAuthz } from '@kbn/core/server'; import { hiddenTypes as filesSavedObjectTypes } from '@kbn/files-plugin/server/saved_objects'; import type { FeaturesPluginSetup } from '@kbn/features-plugin/server'; import type { ProductFeatureKeyType } from '@kbn/security-solution-features'; @@ -21,6 +21,7 @@ import { getCasesFeature, getSecurityFeature, } from '@kbn/security-solution-features/product_features'; +import type { RecursiveReadonly } from '@kbn/utility-types'; import type { ExperimentalFeatures } from '../../../common'; import { APP_ID } from '../../../common'; import { ProductFeatures } from './product_features'; @@ -28,6 +29,9 @@ import type { ProductFeaturesConfigurator } from './types'; import { securityDefaultSavedObjects } from './security_saved_objects'; import { casesApiTags, casesUiCapabilities } from './cases_privileges'; +// The prefix ("securitySolution-") used by all the Security Solution API action privileges. +export const API_ACTION_PREFIX = `${APP_ID}-`; + export class ProductFeaturesService { private securityProductFeatures: ProductFeatures; private casesProductFeatures: ProductFeatures; @@ -116,8 +120,6 @@ export class ProductFeaturesService { return this.productFeatures.has(productFeatureKey); } - public getApiActionName = (apiPrivilege: string) => `api:${APP_ID}-${apiPrivilege}`; - public isActionRegistered(action: string) { return ( this.securityProductFeatures.isActionRegistered(action) || @@ -127,6 +129,9 @@ export class ProductFeaturesService { ); } + public getApiActionName = (apiPrivilege: string) => `api:${API_ACTION_PREFIX}${apiPrivilege}`; + + /** @deprecated Use security.authz.requiredPrivileges instead */ public isApiPrivilegeEnabled(apiPrivilege: string) { return this.isActionRegistered(this.getApiActionName(apiPrivilege)); } @@ -135,14 +140,24 @@ export class ProductFeaturesService { // The `securitySolutionProductFeature:` prefix is used for ProductFeature based control. // Should be used only by routes that do not need RBAC, only direct productFeature control. const APP_FEATURE_TAG_PREFIX = 'securitySolutionProductFeature:'; - // The "access:securitySolution-" prefix is used for API action based control. - // Should be used by routes that need RBAC, extending the `access:` role privilege check from the security plugin. - // An additional check is performed to ensure the privilege has been registered by the productFeature service, - // preventing full access (`*`) roles, such as superuser, from bypassing productFeature controls. + + /** @deprecated Use security.authz.requiredPrivileges instead */ const API_ACTION_TAG_PREFIX = `access:${APP_ID}-`; + const isAuthzEnabled = (authz?: RecursiveReadonly): authz is AuthzEnabled => { + return Boolean((authz as AuthzEnabled)?.requiredPrivileges); + }; + + /** Returns true only if the API privilege is a security action and is disabled */ + const isApiPrivilegeSecurityAndDisabled = (apiPrivilege: string): boolean => { + if (apiPrivilege.startsWith(API_ACTION_PREFIX)) { + return !this.isActionRegistered(`api:${apiPrivilege}`); + } + return false; + }; + http.registerOnPostAuth((request, response, toolkit) => { - for (const tag of request.route.options.tags) { + for (const tag of request.route.options.tags ?? []) { let isEnabled = true; if (tag.startsWith(APP_FEATURE_TAG_PREFIX)) { isEnabled = this.isEnabled( @@ -159,6 +174,36 @@ export class ProductFeaturesService { return response.notFound(); } } + + // This control ensures the action privileges have been registered by the productFeature service, + // preventing full access (`*`) roles, such as superuser, from bypassing productFeature controls. + const authz = request.route.options.security?.authz; + if (isAuthzEnabled(authz)) { + const disabled = authz.requiredPrivileges.some((privilegeEntry) => { + if (typeof privilegeEntry === 'object') { + if (privilegeEntry.allRequired) { + if (privilegeEntry.allRequired.some(isApiPrivilegeSecurityAndDisabled)) { + return true; + } + } + if (privilegeEntry.anyRequired) { + if (privilegeEntry.anyRequired.every(isApiPrivilegeSecurityAndDisabled)) { + return true; + } + } + return false; + } else { + return isApiPrivilegeSecurityAndDisabled(privilegeEntry); + } + }); + if (disabled) { + this.logger.warn( + `Accessing disabled route "${request.url.pathname}${request.url.search}": responding with 404` + ); + return response.notFound(); + } + } + return toolkit.next(); }); } 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 8e50e4590a72f..50e0e0be47cdd 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/constants.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/constants.ts @@ -27,8 +27,6 @@ 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/receiver.ts b/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts index 22f85d19c83d8..4d2ff971eeb62 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts @@ -48,6 +48,7 @@ import type { } from '@kbn/fleet-plugin/server'; import type { ExceptionListClient } from '@kbn/lists-plugin/server'; import moment from 'moment'; +import { DEFAULT_DIAGNOSTIC_INDEX_PATTERN } from '../../../common/endpoint/constants'; import type { ExperimentalFeatures } from '../../../common'; import type { EndpointAppContextService } from '../../endpoint/endpoint_app_context_services'; import { @@ -85,7 +86,6 @@ 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'; import type { TelemetryLogger } from './telemetry_logger'; export interface ITelemetryReceiver { @@ -546,7 +546,7 @@ export class TelemetryReceiver implements ITelemetryReceiver { to: executeTo, } as LogMeta); - let pitId = await this.openPointInTime(DEFAULT_DIAGNOSTIC_INDEX); + let pitId = await this.openPointInTime(DEFAULT_DIAGNOSTIC_INDEX_PATTERN); let fetchMore = true; let searchAfter: SortResults | undefined; 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 index 35f5abeac10af..ec401a093c348 100644 --- 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 @@ -6,11 +6,12 @@ */ import type { Logger } from '@kbn/core/server'; +import { DEFAULT_DIAGNOSTIC_INDEX_PATTERN } from '../../../../common/endpoint/constants'; import type { ITelemetryEventsSender } from '../sender'; import type { ITelemetryReceiver } from '../receiver'; import type { TaskExecutionPeriod } from '../task'; import type { ITaskMetricsService } from '../task_metrics.types'; -import { DEFAULT_DIAGNOSTIC_INDEX, TELEMETRY_CHANNEL_TIMELINE } from '../constants'; +import { TELEMETRY_CHANNEL_TIMELINE } from '../constants'; import { ranges, TelemetryTimelineFetcher, newTelemetryLogger } from '../helpers'; export function createTelemetryDiagnosticTimelineTaskConfig() { @@ -43,7 +44,7 @@ export function createTelemetryDiagnosticTimelineTaskConfig() { const { rangeFrom, rangeTo } = ranges(taskExecutionPeriod); const alerts = await receiver.fetchTimelineAlerts( - DEFAULT_DIAGNOSTIC_INDEX, + DEFAULT_DIAGNOSTIC_INDEX_PATTERN, rangeFrom, rangeTo ); diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 428db0309346d..5b43aabbd0b62 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -19,6 +19,7 @@ import type { ILicense } from '@kbn/licensing-plugin/server'; import type { NewPackagePolicy, UpdatePackagePolicy } from '@kbn/fleet-plugin/common'; import { FLEET_ENDPOINT_PACKAGE } from '@kbn/fleet-plugin/common'; +import { ensureIndicesExistsForPolicies } from './endpoint/migrations/ensure_indices_exists_for_policies'; import { CompleteExternalResponseActionsTask } from './endpoint/lib/response_actions'; import { registerAgentRoutes } from './endpoint/routes/agent'; import { endpointPackagePoliciesStatsSearchStrategyProvider } from './search_strategy/endpoint_package_policies_stats'; @@ -606,8 +607,10 @@ export class Plugin implements ISecuritySolutionPlugin { plugins.fleet .fleetSetupCompleted() .then(async () => { + logger.info('Dependent plugin setup complete'); + if (this.manifestTask) { - logger.info('Dependent plugin setup complete - Starting ManifestTask'); + logger.info('Starting ManifestTask'); await this.manifestTask.start({ taskManager, }); @@ -625,6 +628,10 @@ export class Plugin implements ISecuritySolutionPlugin { ); await turnOffAgentPolicyFeatures(fleetServices, productFeaturesService, logger); + + // Ensure policies have backing DOT indices (We don't need to `await` this. + // It can run in the background) + ensureIndicesExistsForPolicies(this.endpointAppContextService).catch(() => {}); }) .catch(() => {}); 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 e57141b3a5ae7..bd5c29651e26e 100644 --- a/x-pack/plugins/security_solution/server/request_context_factory.ts +++ b/x-pack/plugins/security_solution/server/request_context_factory.ts @@ -225,6 +225,7 @@ export class RequestContextFactory implements IRequestContextFactory { taskManager: startPlugins.taskManager, auditLogger: getAuditLogger(), kibanaVersion: options.kibanaVersion, + config: config.entityAnalytics.entityStore, telemetry: core.analytics, }); }), diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index a84f21c047ea8..f8fdcfcd8f438 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -15,11 +15,7 @@ "public/**/*.json", "../../../typings/**/*" ], - "exclude": [ - "target/**/*", - "**/cypress/**", - "public/management/cypress.config.ts" - ], + "exclude": ["target/**/*", "**/cypress/**", "public/management/cypress.config.ts"], "kbn_references": [ "@kbn/core", { @@ -209,7 +205,6 @@ "@kbn/core-theme-browser", "@kbn/integration-assistant-plugin", "@kbn/avc-banner", - "@kbn/security-solution-common", "@kbn/esql-ast", "@kbn/esql-validation-autocomplete", "@kbn/config", diff --git a/x-pack/plugins/serverless/public/navigation/index.tsx b/x-pack/plugins/serverless/public/navigation/index.tsx index b22b8d51e6166..2ff6e27c664f7 100644 --- a/x-pack/plugins/serverless/public/navigation/index.tsx +++ b/x-pack/plugins/serverless/public/navigation/index.tsx @@ -5,14 +5,31 @@ * 2.0. */ +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; import React, { Suspense, type FC } from 'react'; -import { EuiLoadingSpinner } from '@elastic/eui'; +import { EuiSkeletonRectangle } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import type { Props as NavigationProps } from './navigation'; const SideNavComponentLazy = React.lazy(() => import('./navigation')); export const SideNavComponent: FC = (props) => ( - }> + + } + > ); diff --git a/x-pack/plugins/serverless/public/types.ts b/x-pack/plugins/serverless/public/types.ts index 4627d24659b8e..3a416a676ee92 100644 --- a/x-pack/plugins/serverless/public/types.ts +++ b/x-pack/plugins/serverless/public/types.ts @@ -10,6 +10,7 @@ import type { ChromeSetProjectBreadcrumbsParams, SideNavComponent, NavigationTreeDefinition, + SolutionId, } from '@kbn/core-chrome-browser'; import type { CloudSetup, CloudStart } from '@kbn/cloud-plugin/public'; import type { Observable } from 'rxjs'; @@ -26,7 +27,7 @@ export interface ServerlessPluginStart { ) => void; setProjectHome(homeHref: string): void; initNavigation( - id: string, + id: SolutionId, navigationTree$: Observable, config?: { dataTestSubj?: string; diff --git a/x-pack/plugins/serverless/tsconfig.json b/x-pack/plugins/serverless/tsconfig.json index ce60d39bef0f0..35cc5e554ceb3 100644 --- a/x-pack/plugins/serverless/tsconfig.json +++ b/x-pack/plugins/serverless/tsconfig.json @@ -28,5 +28,6 @@ "@kbn/management-cards-navigation", "@kbn/react-kibana-mount", "@kbn/react-kibana-context-render", + "@kbn/ui-theme", ] } diff --git a/x-pack/plugins/serverless_search/public/plugin.ts b/x-pack/plugins/serverless_search/public/plugin.ts index 491252a6d9e9f..3d246e4be2929 100644 --- a/x-pack/plugins/serverless_search/public/plugin.ts +++ b/x-pack/plugins/serverless_search/public/plugin.ts @@ -149,7 +149,7 @@ export class ServerlessSearchPlugin serverless.setProjectHome(services.searchIndices.startRoute); const navigationTree$ = of(navigationTree()); - serverless.initNavigation('search', navigationTree$, { dataTestSubj: 'svlSearchSideNav' }); + serverless.initNavigation('es', navigationTree$, { dataTestSubj: 'svlSearchSideNav' }); const extendCardNavDefinitions = serverless.getNavigationCards( security.authz.isRoleManagementEnabled() diff --git a/x-pack/plugins/session_view/public/methods/index.tsx b/x-pack/plugins/session_view/public/methods/index.tsx index ad8d77660b3b1..43295737c21f1 100644 --- a/x-pack/plugins/session_view/public/methods/index.tsx +++ b/x-pack/plugins/session_view/public/methods/index.tsx @@ -28,10 +28,7 @@ const SUPPORTED_PACKAGES = [ CLOUD_DEFEND_DATA_SOURCE, AUDITBEAT_DATA_SOURCE, ]; -const INDEX_REGEX = new RegExp( - `([a-z0-9_-]+\:)?[a-z0-9\-.]*(${SUPPORTED_PACKAGES.join('|')})`, - 'i' -); +const INDEX_REGEX = new RegExp(`([a-z0-9_-]+\:)?[a-z0-9-.]*(${SUPPORTED_PACKAGES.join('|')})`, 'i'); export const DEFAULT_INDEX = 'logs-*'; export const CLOUD_DEFEND_INDEX = 'logs-cloud_defend.*'; diff --git a/x-pack/plugins/spaces/common/types/space/v1.ts b/x-pack/plugins/spaces/common/types/space/v1.ts index ebd841e914e69..3d7bb94cf65ab 100644 --- a/x-pack/plugins/spaces/common/types/space/v1.ts +++ b/x-pack/plugins/spaces/common/types/space/v1.ts @@ -5,11 +5,11 @@ * 2.0. */ -import type { OnBoardingDefaultSolution } from '@kbn/cloud-plugin/common'; +import type { SolutionId } from '@kbn/core-chrome-browser'; import type { SOLUTION_VIEW_CLASSIC } from '../../constants'; -export type SolutionView = OnBoardingDefaultSolution | typeof SOLUTION_VIEW_CLASSIC; +export type SolutionView = SolutionId | typeof SOLUTION_VIEW_CLASSIC; /** * A Space. diff --git a/x-pack/plugins/spaces/public/nav_control/__snapshots__/nav_control_popover.test.tsx.snap b/x-pack/plugins/spaces/public/nav_control/__snapshots__/nav_control_popover.test.tsx.snap index af2221e460a32..854beab6dfc93 100644 --- a/x-pack/plugins/spaces/public/nav_control/__snapshots__/nav_control_popover.test.tsx.snap +++ b/x-pack/plugins/spaces/public/nav_control/__snapshots__/nav_control_popover.test.tsx.snap @@ -27,11 +27,17 @@ exports[`NavControlPopover renders without crashing 1`] = ` - +
+
+