diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index 94753297ce85f..9404a7aab18c0 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -521,6 +521,8 @@ enabled: - x-pack/test/security_solution_api_integration/test_suites/detections_response/user_roles/trial_license_complete_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/trial_license_complete_tier/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/trial_license_complete_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/configs/ess.config.ts diff --git a/.buildkite/pipelines/emergency_release.yml b/.buildkite/pipelines/emergency_release.yml index 12ba39e7ce8c2..07ab52417f9a0 100644 --- a/.buildkite/pipelines/emergency_release.yml +++ b/.buildkite/pipelines/emergency_release.yml @@ -1,3 +1,18 @@ +env: + VERSION: ${BUILDKITE_COMMIT:0:12} + EMERGENCY_RELEASE_REMOTE_SERVICE_CONFIG: https://raw.githubusercontent.com/elastic/serverless-gitops/main/gen/gpctl/kibana/emergency.yaml + steps: - - label: Placeholder - command: echo "Hello!" + - label: ':label: Create tag' + command: .buildkite/scripts/serverless/create_deploy_tag/create_fix_tag.sh + if: build.branch =~ /^deploy-fix@.*\$/ + + - wait + + - label: ':argo: Emergency Release' + if: build.branch =~ /^deploy-fix@.*\$/ + trigger: gpctl-promote + build: + env: + REMOTE_SERVICE_CONFIG: "${EMERGENCY_RELEASE_REMOTE_SERVICE_CONFIG}" + SERVICE_COMMIT_HASH: "${VERSION}" diff --git a/.buildkite/pipelines/pull_request/base.yml b/.buildkite/pipelines/pull_request/base.yml index 25a373de87509..c9d755d5d94c8 100644 --- a/.buildkite/pipelines/pull_request/base.yml +++ b/.buildkite/pipelines/pull_request/base.yml @@ -96,7 +96,7 @@ steps: agents: queue: n2-4-spot key: build_api_docs - timeout_in_minutes: 60 + timeout_in_minutes: 90 retry: automatic: - exit_status: '-1' diff --git a/.buildkite/pipelines/pull_request/fips.yml b/.buildkite/pipelines/pull_request/fips.yml new file mode 100644 index 0000000000000..dac30490b3733 --- /dev/null +++ b/.buildkite/pipelines/pull_request/fips.yml @@ -0,0 +1,14 @@ +steps: + - command: .buildkite/scripts/steps/fips/build.sh + label: 'Build FIPS Image' + agents: + queue: n2-2-spot + depends_on: + - build + - quick_checks + timeout_in_minutes: 60 + soft_fail: true + retry: + automatic: + - exit_status: '-1' + limit: 3 diff --git a/.buildkite/pipelines/security_solution/api_integration.yml b/.buildkite/pipelines/security_solution/api_integration.yml index 8e36fbfa2ae4a..b0c7832b967d5 100644 --- a/.buildkite/pipelines/security_solution/api_integration.yml +++ b/.buildkite/pipelines/security_solution/api_integration.yml @@ -1,319 +1,334 @@ steps: - - label: Running exception_workflows:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh exception_workflows:qa:serverless - key: exception_workflows:qa:serverless + - command: .buildkite/scripts/pipelines/security_solution_quality_gate/create_periodic_test_docker_image.sh + label: Build kibana image + key: build_image agents: - queue: n2-4-spot - timeout_in_minutes: 120 + queue: n2-16-spot + timeout_in_minutes: 60 retry: automatic: - - exit_status: '*' - limit: 2 + - exit_status: "-1" + limit: 3 - - label: Running exception_operators_date_numeric_types:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh exception_operators_date_numeric_types:qa:serverless - key: exception_operators_date_numeric_types:qa:serverless + - command: .buildkite/scripts/pipelines/security_solution_quality_gate/upload_image_metadata.sh + label: "Upload runtime info" + key: upload_runtime_info + depends_on: build_image agents: queue: n2-4-spot - timeout_in_minutes: 120 + timeout_in_minutes: 300 retry: automatic: - - exit_status: '*' + - exit_status: "-1" limit: 2 - - label: Running exception_operators_keyword:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh exception_operators_keyword:qa:serverless - key: exception_operators_keyword:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '*' - limit: 2 + - group: "Execute Tests" + depends_on: build_image + steps: + - label: Running exception_workflows:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh exception_workflows:qa:serverless + key: exception_workflows:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "*" + limit: 2 - - label: Running exception_operators_ips:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh exception_operators_ips:qa:serverless - key: exception_operators_ips:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '*' - limit: 2 + - label: Running exception_operators_date_numeric_types:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh exception_operators_date_numeric_types:qa:serverless + key: exception_operators_date_numeric_types:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "*" + limit: 2 - - label: Running exception_operators_long:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh exception_operators_long:qa:serverless - key: exception_operators_long:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running exception_operators_keyword:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh exception_operators_keyword:qa:serverless + key: exception_operators_keyword:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "*" + limit: 2 - - label: Running exception_operators_text:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh exception_operators_text:qa:serverless - key: exception_operators_text:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running exception_operators_ips:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh exception_operators_ips:qa:serverless + key: exception_operators_ips:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "*" + limit: 2 - - label: Running rule_creation:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh rule_creation:qa:serverless - key: rule_creation:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running exception_operators_long:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh exception_operators_long:qa:serverless + key: exception_operators_long:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 - - label: Running rule_creation:essentials:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh rule_creation:essentials:qa:serverless - key: rule_creation:essentials:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running exception_operators_text:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh exception_operators_text:qa:serverless + key: exception_operators_text:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 - - label: Running alerts:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh alerts:qa:serverless - key: alerts:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running rule_creation:essentials:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_creation:essentials:qa:serverless + key: rule_creation:essentials:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '1' + limit: 2 - - label: Running alerts:essentials:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh alerts:essentials:qa:serverless - key: alerts:essentials:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running alerts:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh alerts:qa:serverless + key: alerts:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '1' + limit: 2 - - label: Running actions:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh actions:qa:serverless - key: actions:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running alerts:essentials:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh alerts:essentials:qa:serverless + key: alerts:essentials:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '1' + limit: 2 - - label: Running entity_analytics:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh entity_analytics:qa:serverless - key: entity_analytics:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running actions:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh actions:qa:serverless + key: actions:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '1' + limit: 2 - - label: Running prebuilt_rules_management:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh prebuilt_rules_management:qa:serverless - key: prebuilt_rules_management:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running entity_analytics:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh entity_analytics:qa:serverless + key: entity_analytics:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 - - label: Running prebuilt_rules_bundled_prebuilt_rules_package:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh prebuilt_rules_bundled_prebuilt_rules_package:qa:serverless - key: prebuilt_rules_bundled_prebuilt_rules_package:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running genai:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh genai:qa:serverless + key: genai:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 - - label: Running prebuilt_rules_large_prebuilt_rules_package:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh prebuilt_rules_large_prebuilt_rules_package:qa:serverless - key: prebuilt_rules_large_prebuilt_rules_package:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running prebuilt_rules_management:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh prebuilt_rules_management:qa:serverless + key: prebuilt_rules_management:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 - - label: Running prebuilt_rules_update_prebuilt_rules_package:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh prebuilt_rules_update_prebuilt_rules_package:qa:serverless - key: prebuilt_rules_update_prebuilt_rules_package:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running prebuilt_rules_bundled_prebuilt_rules_package:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh prebuilt_rules_bundled_prebuilt_rules_package:qa:serverless + key: prebuilt_rules_bundled_prebuilt_rules_package:qa:serverless + agents: + queue: n2-4-spot + 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-tests.sh rule_execution_logic:qa:serverless - key: rule_execution_logic:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running prebuilt_rules_large_prebuilt_rules_package:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh prebuilt_rules_large_prebuilt_rules_package:qa:serverless + key: prebuilt_rules_large_prebuilt_rules_package:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 - - label: Running user_roles:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh user_roles:qa:serverless - key: user_roles:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running prebuilt_rules_update_prebuilt_rules_package:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh prebuilt_rules_update_prebuilt_rules_package:qa:serverless + key: prebuilt_rules_update_prebuilt_rules_package:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 - - label: Running telemetry:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh telemetry:qa:serverless - key: telemetry:qa:serverless - agents: - queue: n2-4-spot - 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 + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 - - label: Running rule_delete:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh rule_delete:qa:serverless - key: rule_delete:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running user_roles:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh user_roles:qa:serverless + key: user_roles:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 - - label: Running rule_update:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh rule_update:qa:serverless - key: rule_update:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running telemetry:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh telemetry:qa:serverless + key: telemetry:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 - - label: Running rule_patch:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh rule_patch:qa:serverless - key: rule_patch:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running rule_delete:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_delete:qa:serverless + key: rule_delete:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 - - label: Running rule_import_export:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh rule_import_export:qa:serverless - key: rule_import_export:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running rule_update:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_update:qa:serverless + key: rule_update:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 - - label: Running rule_management:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh rule_management:qa:serverless - key: rule_management:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running rule_patch:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_patch:qa:serverless + key: rule_patch:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 - - label: Running rule_bulk_actions:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh rule_bulk_actions:qa:serverless - key: rule_bulk_actions:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running rule_import_export:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_import_export:qa:serverless + key: rule_import_export:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 - - label: Running rule_read:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh rule_read:qa:serverless - key: rule_read:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running rule_management:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_management:qa:serverless + key: rule_management:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: "1" + limit: 2 - - label: Running rules_management:essentials:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh rules_management:essentials:qa:serverless - key: rules_management:essentials:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running rule_read:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rule_read:qa:serverless + key: rule_read:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '1' + limit: 2 - - label: Running exception_lists_items:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh exception_lists_items:qa:serverless - key: exception_lists_items:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running rules_management:essentials:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh rules_management:essentials:qa:serverless + key: rules_management:essentials:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '1' + limit: 2 - - label: Running lists_items:qa:serverless - command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh lists_items:qa:serverless - key: lists_items:qa:serverless - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '1' - limit: 2 + - label: Running exception_lists_items:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh exception_lists_items:qa:serverless + key: exception_lists_items:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '1' + limit: 2 + + - label: Running lists_items:qa:serverless + command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh lists_items:qa:serverless + key: lists_items:qa:serverless + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '1' + limit: 2 diff --git a/.buildkite/pipelines/security_solution/security_solution_cypress.yml b/.buildkite/pipelines/security_solution/security_solution_cypress.yml index f6fce93c56614..997e607ebb43f 100644 --- a/.buildkite/pipelines/security_solution/security_solution_cypress.yml +++ b/.buildkite/pipelines/security_solution/security_solution_cypress.yml @@ -1,96 +1,122 @@ steps: - - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:explore - label: 'Serverless MKI QA Explore - Security Solution Cypress Tests' + - command: .buildkite/scripts/pipelines/security_solution_quality_gate/create_periodic_test_docker_image.sh + label: Build kibana image + key: build_image agents: - queue: n2-4-spot - # TODO : Revise the timeout when the pipeline will be officially integrated with the quality gate. - timeout_in_minutes: 300 - parallelism: 4 + queue: n2-16-spot + timeout_in_minutes: 60 retry: automatic: - - exit_status: '*' - limit: 1 + - exit_status: "-1" + limit: 3 - - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:investigations - label: 'Serverless MKI QA Investigations - Security Solution Cypress Tests' + - command: .buildkite/scripts/pipelines/security_solution_quality_gate/upload_image_metadata.sh + label: "Upload runtime info" + key: upload_runtime_info + depends_on: build_image agents: queue: n2-4-spot - # TODO : Revise the timeout when the pipeline will be officially integrated with the quality gate. timeout_in_minutes: 300 - parallelism: 8 retry: automatic: - - exit_status: '*' + - exit_status: "*" limit: 1 - - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:rule_management - label: 'Serverless MKI QA Rule Management - Security Solution Cypress Tests' - agents: - queue: n2-4-spot - # TODO : Revise the timeout when the pipeline will be officially integrated with the quality gate. - timeout_in_minutes: 300 - parallelism: 8 - retry: - automatic: - - exit_status: '*' - limit: 1 + - group: "Execute Tests" + depends_on: build_image + steps: + # - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:explore + # label: 'Serverless MKI QA Explore - Security Solution Cypress Tests' + # agents: + # queue: n2-4-spot + # # TODO : Revise the timeout when the pipeline will be officially integrated with the quality gate. + # timeout_in_minutes: 300 + # parallelism: 4 + # retry: + # automatic: + # - exit_status: '*' + # limit: 1 - - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:rule_management:prebuilt_rules - label: 'Serverless MKI QA Rule Management - Prebuilt Rules - Security Solution Cypress Tests' - agents: - queue: n2-4-spot - # TODO : Revise the timeout when the pipeline will be officially integrated with the quality gate. - timeout_in_minutes: 300 - parallelism: 2 - retry: - automatic: - - exit_status: '*' - limit: 1 + # - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:investigations + # label: 'Serverless MKI QA Investigations - Security Solution Cypress Tests' + # agents: + # queue: n2-4-spot + # # TODO : Revise the timeout when the pipeline will be officially integrated with the quality gate. + # timeout_in_minutes: 300 + # parallelism: 8 + # retry: + # automatic: + # - exit_status: '*' + # limit: 1 - - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:detection_engine - label: 'Serverless MKI QA Detection Engine - Security Solution Cypress Tests' - agents: - queue: n2-4-spot - # TODO : Revise the timeout when the pipeline will be officially integrated with the quality gate. - timeout_in_minutes: 300 - parallelism: 8 - retry: - automatic: - - exit_status: '*' - limit: 1 + # - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:rule_management + # label: 'Serverless MKI QA Rule Management - Security Solution Cypress Tests' + # agents: + # queue: n2-4-spot + # # TODO : Revise the timeout when the pipeline will be officially integrated with the quality gate. + # timeout_in_minutes: 300 + # parallelism: 8 + # retry: + # automatic: + # - exit_status: '*' + # limit: 1 - - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:detection_engine:exceptions - label: 'Serverless MKI QA Detection Engine - Exceptions - Security Solution Cypress Tests' - agents: - queue: n2-4-spot - # TODO : Revise the timeout when the pipeline will be officially integrated with the quality gate. - timeout_in_minutes: 300 - parallelism: 6 - retry: - automatic: - - exit_status: '*' - limit: 1 + # - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:rule_management:prebuilt_rules + # label: 'Serverless MKI QA Rule Management - Prebuilt Rules - Security Solution Cypress Tests' + # agents: + # queue: n2-4-spot + # # TODO : Revise the timeout when the pipeline will be officially integrated with the quality gate. + # timeout_in_minutes: 300 + # parallelism: 2 + # retry: + # automatic: + # - exit_status: '*' + # limit: 1 - - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:ai_assistant - label: 'Serverless MKI QA AI Assistant - Security Solution Cypress Tests' - agents: - queue: n2-4-spot - # TODO : Revise the timeout when the pipeline will be officially integrated with the quality gate. - timeout_in_minutes: 300 - parallelism: 1 - retry: - automatic: - - exit_status: '*' - limit: 1 + # - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:detection_engine + # label: 'Serverless MKI QA Detection Engine - Security Solution Cypress Tests' + # agents: + # queue: n2-4-spot + # # TODO : Revise the timeout when the pipeline will be officially integrated with the quality gate. + # timeout_in_minutes: 300 + # parallelism: 8 + # retry: + # automatic: + # - exit_status: '*' + # limit: 1 - - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:entity_analytics - label: 'Serverless MKI QA Entity Analytics - Security Solution Cypress Tests' - agents: - queue: n2-4-spot - # TODO : Revise the timeout when the pipeline will be officially integrated with the quality gate. - timeout_in_minutes: 300 - parallelism: 2 - retry: - automatic: - - exit_status: '*' - limit: 1 \ No newline at end of file + # - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:detection_engine:exceptions + # label: 'Serverless MKI QA Detection Engine - Exceptions - Security Solution Cypress Tests' + # agents: + # queue: n2-4-spot + # # TODO : Revise the timeout when the pipeline will be officially integrated with the quality gate. + # timeout_in_minutes: 300 + # parallelism: 6 + # retry: + # automatic: + # - exit_status: '*' + # limit: 1 + + - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:ai_assistant + label: 'Serverless MKI QA AI Assistant - Security Solution Cypress Tests' + agents: + queue: n2-4-spot + # TODO : Revise the timeout when the pipeline will be officially integrated with the quality gate. + timeout_in_minutes: 300 + parallelism: 1 + retry: + automatic: + - exit_status: '*' + limit: 1 + + # - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:entity_analytics + # label: 'Serverless MKI QA Entity Analytics - Security Solution Cypress Tests' + # agents: + # queue: n2-4-spot + # # TODO : Revise the timeout when the pipeline will be officially integrated with the quality gate. + # timeout_in_minutes: 300 + # parallelism: 2 + # retry: + # automatic: + # - exit_status: '*' + # limit: 1 \ No newline at end of file diff --git a/.buildkite/scripts/build_kibana.sh b/.buildkite/scripts/build_kibana.sh index 66e53f80bdf5a..537bee3a290a2 100755 --- a/.buildkite/scripts/build_kibana.sh +++ b/.buildkite/scripts/build_kibana.sh @@ -31,6 +31,7 @@ if is_pr_with_label "ci:build-cloud-image"; then --docker-tag-qualifier="$GIT_COMMIT" \ --docker-push \ --skip-docker-ubi \ + --skip-docker-fips \ --skip-docker-ubuntu \ --skip-docker-serverless \ --skip-docker-contexts diff --git a/.buildkite/scripts/common/activate_service_account.sh b/.buildkite/scripts/common/activate_service_account.sh new file mode 100755 index 0000000000000..e5cd116a7bce1 --- /dev/null +++ b/.buildkite/scripts/common/activate_service_account.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source "$(dirname "${BASH_SOURCE[0]}")/vault_fns.sh" + +BUCKET_OR_EMAIL="${1:-}" +GCLOUD_EMAIL_POSTFIX="elastic-kibana-ci.iam.gserviceaccount.com" +GCLOUD_SA_PROXY_EMAIL="kibana-ci-sa-proxy@$GCLOUD_EMAIL_POSTFIX" + +if [[ -z "$BUCKET_OR_EMAIL" ]]; then + echo "Usage: $0 " + exit 1 +elif [[ "$BUCKET_OR_EMAIL" == "--unset-impersonation" ]]; then + echo "Unsetting impersonation" + gcloud config unset auth/impersonate_service_account + exit 0 +elif [[ "$BUCKET_OR_EMAIL" == "--logout-gcloud" ]]; then + echo "Logging out of gcloud" + if [[ -x "$(command -v gcloud)" ]] && [[ "$(gcloud auth list 2>/dev/null | grep $GCLOUD_SA_PROXY_EMAIL)" != "" ]]; then + gcloud auth revoke $GCLOUD_SA_PROXY_EMAIL --no-user-output-enabled + fi + exit 0 +fi + +CURRENT_GCLOUD_USER=$(gcloud auth list --filter="status=ACTIVE" --format="value(account)") + +# Verify that the service account proxy is activated +if [[ "$CURRENT_GCLOUD_USER" != "$GCLOUD_SA_PROXY_EMAIL" ]]; then + if [[ -x "$(command -v gcloud)" ]]; then + if [[ -z "${KIBANA_SERVICE_ACCOUNT_PROXY_KEY:-}" ]]; then + echo "KIBANA_SERVICE_ACCOUNT_PROXY_KEY is not set, cannot activate service account $GCLOUD_SA_PROXY_EMAIL." + exit 1 + fi + + AUTH_RESULT=$(gcloud auth activate-service-account --key-file="$KIBANA_SERVICE_ACCOUNT_PROXY_KEY" || "FAILURE") + if [[ "$AUTH_RESULT" == "FAILURE" ]]; then + echo "Failed to activate service account $GCLOUD_SA_PROXY_EMAIL." + exit 1 + else + echo "Activated service account $GCLOUD_SA_PROXY_EMAIL" + fi + else + echo "gcloud is not installed, cannot activate service account $GCLOUD_SA_PROXY_EMAIL." + exit 1 + fi +fi + +# Check if the arg is a service account e-mail or a bucket name +EMAIL="" +if [[ "$BUCKET_OR_EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then + EMAIL="$BUCKET_OR_EMAIL" +elif [[ "$BUCKET_OR_EMAIL" =~ ^gs://* ]]; then + BUCKET_NAME="${BUCKET_OR_EMAIL:5}" +else + BUCKET_NAME="$BUCKET_OR_EMAIL" +fi + +if [[ -z "$EMAIL" ]]; then + case "$BUCKET_NAME" in + "elastic-kibana-coverage-live") + EMAIL="kibana-ci-access-coverage@$GCLOUD_EMAIL_POSTFIX" + ;; + "kibana-ci-es-snapshots-daily") + EMAIL="kibana-ci-access-es-snapshots@$GCLOUD_EMAIL_POSTFIX" + ;; + "kibana-so-types-snapshots") + EMAIL="kibana-ci-access-so-snapshots@$GCLOUD_EMAIL_POSTFIX" + ;; + "kibana-performance") + EMAIL="kibana-ci-access-perf-stats@$GCLOUD_EMAIL_POSTFIX" + ;; + "ci-artifacts.kibana.dev") + EMAIL="kibana-ci-access-artifacts@$GCLOUD_EMAIL_POSTFIX" + ;; + *) + EMAIL="$BUCKET_NAME@$GCLOUD_EMAIL_POSTFIX" + ;; + esac +fi + +# Activate the service account +echo "Impersonating $EMAIL" +gcloud config set auth/impersonate_service_account "$EMAIL" +echo "Activated service account $EMAIL" diff --git a/.buildkite/scripts/common/setup_bazel.sh b/.buildkite/scripts/common/setup_bazel.sh index ea3c2453de6d2..5461f713e0af3 100755 --- a/.buildkite/scripts/common/setup_bazel.sh +++ b/.buildkite/scripts/common/setup_bazel.sh @@ -2,6 +2,8 @@ source .buildkite/scripts/common/util.sh +echo '--- Setting up bazel' + echo "[bazel] writing .bazelrc" cat < $KIBANA_DIR/.bazelrc # Generated by .buildkite/scripts/common/setup_bazel.sh @@ -27,16 +29,16 @@ if [[ "$BAZEL_CACHE_MODE" == "gcs" ]]; then echo "[bazel] using GCS bucket: $BAZEL_BUCKET" -cat <> $KIBANA_DIR/.bazelrc + cat <> $KIBANA_DIR/.bazelrc build --remote_cache=https://storage.googleapis.com/$BAZEL_BUCKET - build --google_default_credentials + build --google_credentials=$BAZEL_REMOTE_CACHE_CREDENTIALS_FILE EOF fi if [[ "$BAZEL_CACHE_MODE" == "populate-local-gcs" ]]; then echo "[bazel] enabling caching with GCS buckets for local dev" -cat <> $KIBANA_DIR/.bazelrc + cat <> $KIBANA_DIR/.bazelrc build --remote_cache=https://storage.googleapis.com/kibana-local-bazel-remote-cache build --google_credentials=$BAZEL_LOCAL_DEV_CACHE_CREDENTIALS_FILE EOF diff --git a/.buildkite/scripts/common/util.sh b/.buildkite/scripts/common/util.sh index f80c89678c221..818d712fd2aa8 100755 --- a/.buildkite/scripts/common/util.sh +++ b/.buildkite/scripts/common/util.sh @@ -1,5 +1,7 @@ #!/usr/bin/env bash +source "$(dirname "${BASH_SOURCE[0]}")/vault_fns.sh" + is_pr() { [[ "${GITHUB_PR_NUMBER-}" ]] && return false @@ -170,48 +172,3 @@ npm_install_global() { download_artifact() { retry 3 1 timeout 3m buildkite-agent artifact download "$@" } - -# TODO: remove after https://github.com/elastic/kibana-operations/issues/15 is done -if [[ "${VAULT_ADDR:-}" == *"secrets.elastic.co"* ]]; then - VAULT_PATH_PREFIX="secret/kibana-issues/dev" - VAULT_KV_PREFIX="secret/kibana-issues/dev" - IS_LEGACY_VAULT_ADDR=true -else - VAULT_PATH_PREFIX="secret/ci/elastic-kibana" - VAULT_KV_PREFIX="kv/ci-shared/kibana-deployments" - IS_LEGACY_VAULT_ADDR=false -fi -export IS_LEGACY_VAULT_ADDR - -vault_get() { - key_path=$1 - field=$2 - - fullPath="$VAULT_PATH_PREFIX/$key_path" - - if [[ -z "${2:-}" || "${2:-}" =~ ^-.* ]]; then - retry 5 5 vault read "$fullPath" "${@:2}" - else - retry 5 5 vault read -field="$field" "$fullPath" "${@:3}" - fi -} - -vault_set() { - key_path=$1 - shift - fields=("$@") - - - fullPath="$VAULT_PATH_PREFIX/$key_path" - - # shellcheck disable=SC2068 - retry 5 5 vault write "$fullPath" ${fields[@]} -} - -vault_kv_set() { - kv_path=$1 - shift - fields=("$@") - - vault kv put "$VAULT_KV_PREFIX/$kv_path" "${fields[@]}" -} diff --git a/.buildkite/scripts/common/vault_fns.sh b/.buildkite/scripts/common/vault_fns.sh new file mode 100644 index 0000000000000..a7b92a4b05d6d --- /dev/null +++ b/.buildkite/scripts/common/vault_fns.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +# TODO: remove after https://github.com/elastic/kibana-operations/issues/15 is done +if [[ "${VAULT_ADDR:-}" == *"secrets.elastic.co"* ]]; then + VAULT_PATH_PREFIX="secret/kibana-issues/dev" + VAULT_KV_PREFIX="secret/kibana-issues/dev" + IS_LEGACY_VAULT_ADDR=true +else + VAULT_PATH_PREFIX="secret/ci/elastic-kibana" + VAULT_KV_PREFIX="kv/ci-shared/kibana-deployments" + IS_LEGACY_VAULT_ADDR=false +fi +export IS_LEGACY_VAULT_ADDR + +retry() { + local retries=$1; shift + local delay=$1; shift + local attempts=1 + + until "$@"; do + retry_exit_status=$? + echo "Exited with $retry_exit_status" >&2 + if (( retries == "0" )); then + return $retry_exit_status + elif (( attempts == retries )); then + echo "Failed $attempts retries" >&2 + return $retry_exit_status + else + echo "Retrying $((retries - attempts)) more times..." >&2 + attempts=$((attempts + 1)) + sleep "$delay" + fi + done +} + +vault_get() { + key_path=${1:-} + field=${2:-} + + fullPath="$VAULT_PATH_PREFIX/$key_path" + + if [[ -z "$field" || "$field" =~ ^-.* ]]; then + retry 5 5 vault read "$fullPath" "${@:2}" + else + retry 5 5 vault read -field="$field" "$fullPath" "${@:3}" + fi +} + +vault_set() { + key_path=$1 + shift + fields=("$@") + + + fullPath="$VAULT_PATH_PREFIX/$key_path" + + # shellcheck disable=SC2068 + retry 5 5 vault write "$fullPath" ${fields[@]} +} + +vault_kv_set() { + kv_path=$1 + shift + fields=("$@") + + vault kv put "$VAULT_KV_PREFIX/$kv_path" "${fields[@]}" +} diff --git a/.buildkite/scripts/lifecycle/post_command.sh b/.buildkite/scripts/lifecycle/post_command.sh index 620041151e583..479608ca33259 100755 --- a/.buildkite/scripts/lifecycle/post_command.sh +++ b/.buildkite/scripts/lifecycle/post_command.sh @@ -2,6 +2,10 @@ set -euo pipefail +echo '--- Log out of gcloud' +./.buildkite/scripts/common/activate_service_account.sh --unset-impersonation || echo "Failed to unset impersonation" +./.buildkite/scripts/common/activate_service_account.sh --logout-gcloud || echo "Failed to log out of gcloud" + echo '--- Agent Debug Info' ts-node .buildkite/scripts/lifecycle/print_agent_links.ts || true diff --git a/.buildkite/scripts/lifecycle/pre_command.sh b/.buildkite/scripts/lifecycle/pre_command.sh index c14cb2c578a8a..966ba22c1272c 100755 --- a/.buildkite/scripts/lifecycle/pre_command.sh +++ b/.buildkite/scripts/lifecycle/pre_command.sh @@ -167,6 +167,16 @@ BAZEL_LOCAL_DEV_CACHE_CREDENTIALS_FILE="$HOME/.kibana-ci-bazel-remote-cache-loca export BAZEL_LOCAL_DEV_CACHE_CREDENTIALS_FILE vault_get kibana-ci-bazel-remote-cache-local-dev service_account_json > "$BAZEL_LOCAL_DEV_CACHE_CREDENTIALS_FILE" +# Export key for accessing bazel remote cache's GCS bucket +BAZEL_REMOTE_CACHE_CREDENTIALS_FILE="$HOME/.kibana-ci-bazel-remote-cache-gcs.json" +export BAZEL_REMOTE_CACHE_CREDENTIALS_FILE +vault_get kibana-ci-bazel-remote-cache-sa-key key | base64 -d > "$BAZEL_REMOTE_CACHE_CREDENTIALS_FILE" + +# Setup GCS Service Account Proxy for CI +KIBANA_SERVICE_ACCOUNT_PROXY_KEY="$(mktemp -d)/kibana-gcloud-service-account.json" +export KIBANA_SERVICE_ACCOUNT_PROXY_KEY +vault_get kibana-ci-sa-proxy-key key | base64 -d > "$KIBANA_SERVICE_ACCOUNT_PROXY_KEY" + PIPELINE_PRE_COMMAND=${PIPELINE_PRE_COMMAND:-".buildkite/scripts/lifecycle/pipelines/$BUILDKITE_PIPELINE_SLUG/pre_command.sh"} if [[ -f "$PIPELINE_PRE_COMMAND" ]]; then source "$PIPELINE_PRE_COMMAND" diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.ts b/.buildkite/scripts/pipelines/pull_request/pipeline.ts index 07bd9472f5242..25539aecb0707 100644 --- a/.buildkite/scripts/pipelines/pull_request/pipeline.ts +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.ts @@ -139,6 +139,10 @@ const uploadPipeline = (pipelineContent: string | object) => { pipeline.push(getPipeline('.buildkite/pipelines/pull_request/deploy_cloud.yml')); } + if (GITHUB_PR_LABELS.includes('ci:build-docker-fips')) { + pipeline.push(getPipeline('.buildkite/pipelines/pull_request/fips.yml')); + } + if ( GITHUB_PR_LABELS.includes('ci:project-deploy-elasticsearch') || GITHUB_PR_LABELS.includes('ci:project-deploy-observability') || diff --git a/.buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh b/.buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh similarity index 67% rename from .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh rename to .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh index 1e809b6da6fc4..096709dc5da43 100755 --- a/.buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh +++ b/.buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh @@ -15,15 +15,33 @@ cd x-pack/test/security_solution_api_integration set +e QA_API_KEY=$(vault_get security-solution-quality-gate qa_api_key) +QA_CONSOLE_URL=$(vault_get security-solution-quality-gate qa_console_url) # Generate a random 5-digit number random_number=$((10000 + $RANDOM % 90000)) -ENVIRONMENT_DETAILS=$(curl --location 'https://global.qa.cld.elstc.co/api/v1/serverless/projects/security' \ +if [ -z "${KIBANA_MKI_USE_LATEST_COMMIT+x}" ] || [ "$KIBANA_MKI_USE_LATEST_COMMIT" = "0" ]; then + ENVIRONMENT_DETAILS=$(curl --location "$QA_CONSOLE_URL/api/v1/serverless/projects/security" \ --header "Authorization: ApiKey $QA_API_KEY" \ --header 'Content-Type: application/json' \ --data '{ - "name": "ftr-integration-tests-'$random_number'", - "region_id": "aws-eu-west-1"}' | jq '.') + "name": "ftr-integration-tests-'$random_number'", + "region_id": "aws-eu-west-1"}' | jq '.') +else + KBN_COMMIT_HASH=${BUILDKITE_COMMIT:0:12} + ENVIRONMENT_DETAILS=$(curl --location "$QA_CONSOLE_URL/api/v1/serverless/projects/security" \ + --header "Authorization: ApiKey $QA_API_KEY" \ + --header 'Content-Type: application/json' \ + --data '{ + "name": "ftr-integration-tests-'$random_number'", + "region_id": "aws-eu-west-1", + "overrides": { + "kibana": { + "docker_image" : "docker.elastic.co/kibana-ci/kibana-serverless:sec-sol-qg-'$KBN_COMMIT_HASH'" + } + } + }' | jq '.') +fi + NAME=$(echo $ENVIRONMENT_DETAILS | jq -r '.name') ID=$(echo $ENVIRONMENT_DETAILS | jq -r '.id') ES_URL=$(echo $ENVIRONMENT_DETAILS | jq -r '.endpoints.elasticsearch') @@ -33,7 +51,7 @@ KB_URL=$(echo $ENVIRONMENT_DETAILS | jq -r '.endpoints.kibana') sleep 5 # Resetting the credentials of the elastic user in the project -CREDS_BODY=$(curl -s --location --request POST "https://global.qa.cld.elstc.co/api/v1/serverless/projects/security/$ID/_reset-credentials" \ +CREDS_BODY=$(curl -s --location --request POST "$QA_CONSOLE_URL/api/v1/serverless/projects/security/$ID/_reset-credentials" \ --header "Authorization: ApiKey $QA_API_KEY" \ --header 'Content-Type: application/json' | jq '.') USERNAME=$(echo $CREDS_BODY | jq -r '.username') @@ -76,7 +94,7 @@ TEST_CLOUD=1 TEST_ES_URL="https://elastic:$PASSWORD@$FORMATTED_ES_URL:443" TEST_ cmd_status=$? echo "Exit code with status: $cmd_status" -curl --location --request DELETE "https://global.qa.cld.elstc.co/api/v1/serverless/projects/security/$ID" \ +curl --location --request DELETE "$QA_CONSOLE_URL/api/v1/serverless/projects/security/$ID" \ --header "Authorization: ApiKey $QA_API_KEY" exit $cmd_status diff --git a/.buildkite/scripts/pipelines/security_solution_quality_gate/create_periodic_test_docker_image.sh b/.buildkite/scripts/pipelines/security_solution_quality_gate/create_periodic_test_docker_image.sh new file mode 100644 index 0000000000000..4e459e23ce25b --- /dev/null +++ b/.buildkite/scripts/pipelines/security_solution_quality_gate/create_periodic_test_docker_image.sh @@ -0,0 +1,83 @@ +#!/bin/bash +if [ -z "${KIBANA_MKI_USE_LATEST_COMMIT+x}" ] || [ "$KIBANA_MKI_USE_LATEST_COMMIT" = "0" ]; then + echo "As we not testing against latest kibana image, this step is exiting with exit code 0" + exit 0 +fi + + +.buildkite/scripts/bootstrap.sh + +source .buildkite/scripts/steps/artifacts/env.sh + +GIT_ABBREV_COMMIT=${BUILDKITE_COMMIT:0:12} +KIBANA_IMAGE_TAG="sec-sol-qg-$GIT_ABBREV_COMMIT" + + +KIBANA_BASE_IMAGE="docker.elastic.co/kibana-ci/kibana-serverless" +export KIBANA_IMAGE="$KIBANA_BASE_IMAGE:$KIBANA_IMAGE_TAG" + +echo "--- Verify manifest does not already exist" +echo "$KIBANA_DOCKER_PASSWORD" | docker login -u "$KIBANA_DOCKER_USERNAME" --password-stdin docker.elastic.co +trap 'docker logout docker.elastic.co' EXIT + +echo "Checking manifest for $KIBANA_IMAGE" +if docker manifest inspect $KIBANA_IMAGE &> /dev/null; then + echo "Manifest already exists, exiting" + exit 0 +fi + +docker pull $KIBANA_BASE_IMAGE:latest + +echo "--- Build images" +node scripts/build \ + --debug \ + --release \ + --docker-cross-compile \ + --docker-images \ + --docker-namespace="kibana-ci" \ + --docker-tag="$KIBANA_IMAGE_TAG" \ + --skip-docker-ubuntu \ + --skip-docker-ubi \ + --skip-docker-cloud \ + --skip-docker-contexts \ + --skip-cdn-assets + +echo "--- Tag images" +docker rmi "$KIBANA_IMAGE" +docker load < "target/kibana-serverless-$BASE_VERSION-docker-image.tar.gz" +docker tag "$KIBANA_IMAGE" "$KIBANA_IMAGE-amd64" + +docker rmi "$KIBANA_IMAGE" +docker load < "target/kibana-serverless-$BASE_VERSION-docker-image-aarch64.tar.gz" +docker tag "$KIBANA_IMAGE" "$KIBANA_IMAGE-arm64" + +echo "--- Push images" +docker image push "$KIBANA_IMAGE-arm64" +docker image push "$KIBANA_IMAGE-amd64" + +echo "--- Create and push manifests" +docker manifest create \ + "$KIBANA_IMAGE" \ + --amend "$KIBANA_IMAGE-arm64" \ + --amend "$KIBANA_IMAGE-amd64" +docker manifest push "$KIBANA_IMAGE" + +if [[ "$BUILDKITE_BRANCH" == "$KIBANA_BASE_BRANCH" ]] && [[ "${BUILDKITE_PULL_REQUEST:-false}" == "false" ]]; then + docker manifest create \ + "$KIBANA_BASE_IMAGE:latest" \ + --amend "$KIBANA_IMAGE-arm64" \ + --amend "$KIBANA_IMAGE-amd64" + docker manifest push "$KIBANA_BASE_IMAGE:latest" +fi + +docker logout docker.elastic.co + +cat << EOF | buildkite-agent annotate --style "info" --context image + ### Serverless Images + + Manifest: \`$KIBANA_IMAGE\` + + AMD64: \`$KIBANA_IMAGE-amd64\` + + ARM64: \`$KIBANA_IMAGE-arm64\` +EOF diff --git a/.buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh b/.buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh index e4b1701f161eb..8cce28a1401df 100755 --- a/.buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh +++ b/.buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh @@ -22,7 +22,14 @@ vault_get security-quality-gate/role-users data -format=json > .ftr/role_users.j cd x-pack/test/security_solution_cypress set +e +if [ -z "${KIBANA_MKI_USE_LATEST_COMMIT+x}" ] || [ "$KIBANA_MKI_USE_LATEST_COMMIT" = "0" ]; then + KIBANA_OVERRIDE_FLAG=0 +else + KIBANA_OVERRIDE_FLAG=1 +fi + QA_API_KEY=$(vault_get security-solution-quality-gate qa_api_key) +QA_CONSOLE_URL=$(vault_get security-solution-quality-gate qa_console_url) BK_ANALYTICS_API_KEY=$(vault_get security-solution-quality-gate serverless-sec-sol-cypress-bk-api-key) -BK_ANALYTICS_API_KEY=$BK_ANALYTICS_API_KEY CLOUD_QA_API_KEY=$QA_API_KEY yarn $1; status=$?; yarn junit:merge || :; exit $status \ No newline at end of file +QA_CONSOLE_URL=$QA_CONSOLE_URL KIBANA_MKI_USE_LATEST_COMMIT=$KIBANA_OVERRIDE_FLAG BK_ANALYTICS_API_KEY=$BK_ANALYTICS_API_KEY CLOUD_QA_API_KEY=$QA_API_KEY yarn $1; status=$?; yarn junit:merge || :; exit $status diff --git a/.buildkite/scripts/pipelines/security_solution_quality_gate/upload_image_metadata.sh b/.buildkite/scripts/pipelines/security_solution_quality_gate/upload_image_metadata.sh new file mode 100644 index 0000000000000..c1a22d221cafc --- /dev/null +++ b/.buildkite/scripts/pipelines/security_solution_quality_gate/upload_image_metadata.sh @@ -0,0 +1,27 @@ +#!/bin/bash +echo "$KIBANA_DOCKER_PASSWORD" | docker login -u "$KIBANA_DOCKER_USERNAME" --password-stdin docker.elastic.co + +KIBANA_BASE_IMAGE="docker.elastic.co/kibana-ci/kibana-serverless" +KIBANA_CURRENT_COMMIT=${KIBANA_BASE_IMAGE}:sec-sol-qg-${BUILDKITE_COMMIT:0:12} +KIBANA_LATEST=${KIBANA_BASE_IMAGE}:latest + +if [ "$KIBANA_MKI_USE_LATEST_COMMIT" = "1" ]; then + KBN_IMAGE=${KIBANA_CURRENT_COMMIT} +else + KBN_IMAGE=${KIBANA_LATEST} +fi + +docker pull ${KBN_IMAGE} +build_date=$(docker inspect ${KBN_IMAGE} | jq -r '.[0].Config.Labels."org.label-schema.build-date"') +vcs_ref=$(docker inspect ${KBN_IMAGE} | jq -r '.[0].Config.Labels."org.label-schema.vcs-ref"') +vcs_url=$(docker inspect ${KBN_IMAGE} | jq -r '.[0].Config.Labels."org.label-schema.vcs-url"') +version=$(docker inspect ${KBN_IMAGE} | jq -r '.[0].Config.Labels."org.label-schema.version"') + +markdown_text=""" + # Kibana Container Metadata + - Build Date : $build_date + - Github Commit Hash : $vcs_ref + - Github Repo : $vcs_url + - Version : $version +""" +echo "${markdown_text//[*\\_]/\\&}" | buildkite-agent annotate --style "info" \ No newline at end of file diff --git a/.buildkite/scripts/serverless/create_deploy_tag/create_fix_tag.sh b/.buildkite/scripts/serverless/create_deploy_tag/create_fix_tag.sh new file mode 100755 index 0000000000000..ed1fab966ac49 --- /dev/null +++ b/.buildkite/scripts/serverless/create_deploy_tag/create_fix_tag.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +set -euo pipefail + +echo "--- Verify $BUILDKITE_COMMIT exists in $BUILDKITE_BRANCH" +# Step 1: Check if the commit is in the specific named branch +if git merge-base --is-ancestor $BUILDKITE_COMMIT $BUILDKITE_BRANCH; then + echo "Commit $BUILDKITE_COMMIT is part of the $BUILDKITE_BRANCH branch" + + # Step 2: Check if the commit is also part of any other branches + # This command lists all branches containing the commit and counts them + branches_containing_commit=$(git branch --contains $BUILDKITE_COMMIT | wc -l) + + # If the commit is in more than one branch, exit with non-zero + if [[ $branches_containing_commit -gt 1 ]]; then + echo "Error: Commit $BUILDKITE_COMMIT is part of multiple branches." + exit 1 + else + echo "Commit $BUILDKITE_COMMIT is uniquely part of the $BUILDKITE_BRANCH branch." + fi +else + echo "Commit $BUILDKITE_COMMIT is not part of the $BUILDKITE_BRANCH branch." + exit 1 +fi + +echo "--- Create tag $BUILDKITE_BRANCH" +git tag "$BUILDKITE_BRANCH" "$BUILDKITE_COMMIT" + +echo "--- Push tag $BUILDKITE_BRANCH" +git push origin tag "$BUILDKITE_BRANCH" diff --git a/.buildkite/scripts/steps/archive_so_migration_snapshot.sh b/.buildkite/scripts/steps/archive_so_migration_snapshot.sh index 3db3da975b41b..1c791608709c7 100755 --- a/.buildkite/scripts/steps/archive_so_migration_snapshot.sh +++ b/.buildkite/scripts/steps/archive_so_migration_snapshot.sh @@ -3,15 +3,16 @@ set -euo pipefail .buildkite/scripts/bootstrap.sh -SO_MIGRATIONS_SNAPSHOT_FOLDER=kibana-so-types-snapshots +SO_MIGRATIONS_SNAPSHOT_BUCKET="gs://kibana-so-types-snapshots" SNAPSHOT_FILE_PATH="${1:-target/plugin_so_types_snapshot.json}" echo "--- Creating snapshot of Saved Object migration info" node scripts/snapshot_plugin_types snapshot --outputPath "$SNAPSHOT_FILE_PATH" echo "--- Uploading as ${BUILDKITE_COMMIT}.json" -SNAPSHOT_PATH="${SO_MIGRATIONS_SNAPSHOT_FOLDER}/${BUILDKITE_COMMIT}.json" -gsutil cp "$SNAPSHOT_FILE_PATH" "gs://$SNAPSHOT_PATH" +SNAPSHOT_PATH="${SO_MIGRATIONS_SNAPSHOT_BUCKET}/${BUILDKITE_COMMIT}.json" +.buildkite/scripts/common/activate_service_account.sh "$SO_MIGRATIONS_SNAPSHOT_BUCKET" +gsutil cp "$SNAPSHOT_FILE_PATH" "$SNAPSHOT_PATH" buildkite-agent annotate --context so_migration_snapshot --style success \ 'Saved Object type snapshot is available at '"$SNAPSHOT_PATH"'' diff --git a/.buildkite/scripts/steps/artifacts/docker_image.sh b/.buildkite/scripts/steps/artifacts/docker_image.sh index f6ed33bbce0d3..6e07cec0947ba 100755 --- a/.buildkite/scripts/steps/artifacts/docker_image.sh +++ b/.buildkite/scripts/steps/artifacts/docker_image.sh @@ -36,6 +36,7 @@ node scripts/build \ --docker-tag="$KIBANA_IMAGE_TAG" \ --skip-docker-ubuntu \ --skip-docker-ubi \ + --skip-docker-fips \ --skip-docker-cloud \ --skip-docker-contexts diff --git a/.buildkite/scripts/steps/cloud/build_and_deploy.sh b/.buildkite/scripts/steps/cloud/build_and_deploy.sh index 15de3aa8614b8..6a7a95f8eaf10 100755 --- a/.buildkite/scripts/steps/cloud/build_and_deploy.sh +++ b/.buildkite/scripts/steps/cloud/build_and_deploy.sh @@ -42,6 +42,7 @@ else --docker-tag-qualifier="$GIT_COMMIT" \ --docker-push \ --skip-docker-ubi \ + --skip-docker-fips \ --skip-docker-ubuntu \ --skip-docker-serverless \ --skip-docker-contexts diff --git a/.buildkite/scripts/steps/code_coverage/reporting/downloadPrevSha.sh b/.buildkite/scripts/steps/code_coverage/reporting/downloadPrevSha.sh index af7824841ef28..a77cfbef54d55 100755 --- a/.buildkite/scripts/steps/code_coverage/reporting/downloadPrevSha.sh +++ b/.buildkite/scripts/steps/code_coverage/reporting/downloadPrevSha.sh @@ -6,6 +6,7 @@ set -euo pipefail gsutil -m cp -r gs://elastic-bekitzur-kibana-coverage-live/previous_pointer/previous.txt . || echo "### Previous Pointer NOT FOUND?" # TODO: Activate after the above is removed +#.buildkite/scripts/common/activate_service_account.sh gs://elastic-kibana-coverage-live #gsutil -m cp -r gs://elastic-kibana-coverage-live/previous_pointer/previous.txt . || echo "### Previous Pointer NOT FOUND?" if [ -e ./previous.txt ]; then diff --git a/.buildkite/scripts/steps/code_coverage/reporting/uploadPrevSha.sh b/.buildkite/scripts/steps/code_coverage/reporting/uploadPrevSha.sh index 26d84fa7d6024..42ef5faa5cd3d 100755 --- a/.buildkite/scripts/steps/code_coverage/reporting/uploadPrevSha.sh +++ b/.buildkite/scripts/steps/code_coverage/reporting/uploadPrevSha.sh @@ -12,4 +12,5 @@ collectPrevious # TODO: Safe to remove this after 2024-03-01 (https://github.com/elastic/kibana/issues/175904) gsutil cp previous.txt gs://elastic-bekitzur-kibana-coverage-live/previous_pointer/ +.buildkite/scripts/common/activate_service_account.sh gs://elastic-kibana-coverage-live gsutil cp previous.txt gs://elastic-kibana-coverage-live/previous_pointer/ diff --git a/.buildkite/scripts/steps/code_coverage/reporting/uploadStaticSite.sh b/.buildkite/scripts/steps/code_coverage/reporting/uploadStaticSite.sh index 20e85a51c3776..5bd0c07cc9b9b 100755 --- a/.buildkite/scripts/steps/code_coverage/reporting/uploadStaticSite.sh +++ b/.buildkite/scripts/steps/code_coverage/reporting/uploadStaticSite.sh @@ -27,5 +27,6 @@ uploadRest() { echo "--- Uploading static site" +.buildkite/scripts/common/activate_service_account.sh gs://elastic-kibana-coverage-live uploadBase uploadRest diff --git a/.buildkite/scripts/steps/demo_env/kibana.sh b/.buildkite/scripts/steps/demo_env/kibana.sh index 0483c038aaac4..e8dacdfb94e53 100755 --- a/.buildkite/scripts/steps/demo_env/kibana.sh +++ b/.buildkite/scripts/steps/demo_env/kibana.sh @@ -9,7 +9,7 @@ source "$(dirname "${0}")/config.sh" export KIBANA_IMAGE="gcr.io/elastic-kibana-184716/demo/kibana:$DEPLOYMENT_NAME-$(git rev-parse HEAD)" echo '--- Build Kibana' -node scripts/build --debug --docker-images --example-plugins --skip-docker-ubi --skip-docker-cloud --skip-docker-serverless --skip-docker-contexts +node scripts/build --debug --docker-images --example-plugins --skip-docker-ubi --skip-docker-fips --skip-docker-cloud --skip-docker-serverless --skip-docker-contexts echo '--- Build Docker image with example plugins' cd target/example_plugins diff --git a/.buildkite/scripts/steps/es_serverless/promote_es_serverless_image.sh b/.buildkite/scripts/steps/es_serverless/promote_es_serverless_image.sh index d99cff34f6fbb..6a59959ea6fed 100755 --- a/.buildkite/scripts/steps/es_serverless/promote_es_serverless_image.sh +++ b/.buildkite/scripts/steps/es_serverless/promote_es_serverless_image.sh @@ -7,9 +7,6 @@ source .buildkite/scripts/common/util.sh BASE_ES_SERVERLESS_REPO=docker.elastic.co/elasticsearch-ci/elasticsearch-serverless TARGET_IMAGE=docker.elastic.co/kibana-ci/elasticsearch-serverless:latest-verified -ES_SERVERLESS_BUCKET=kibana-ci-es-serverless-images -MANIFEST_FILE_NAME=latest-verified.json - SOURCE_IMAGE_OR_TAG=$1 if [[ $SOURCE_IMAGE_OR_TAG =~ :[a-zA-Z_-]+$ ]]; then # $SOURCE_IMAGE_OR_TAG was a full image @@ -67,36 +64,6 @@ docker logout docker.elastic.co echo "Image push to $TARGET_IMAGE successful." echo "Promotion successful! Henceforth, thou shall be named Sir $TARGET_IMAGE" -MANIFEST_UPLOAD_PATH="Skipped" -if [[ "${PUBLISH_MANIFEST:-}" =~ ^(1|true)$ && "$SOURCE_IMAGE_OR_TAG" =~ ^git-[0-9a-fA-F]{12}$ ]]; then - echo "--- Uploading latest-verified manifest to GCS" - cat << EOT >> $MANIFEST_FILE_NAME -{ - "build_url": "$BUILDKITE_BUILD_URL", - "kibana_commit": "$BUILDKITE_COMMIT", - "kibana_branch": "$BUILDKITE_BRANCH", - "elasticsearch_serverless_tag": "$SOURCE_IMAGE_OR_TAG", - "elasticsearch_serverless_image_url": "$SOURCE_IMAGE", - "elasticsearch_serverless_commit": "TODO: this currently can't be decided", - "elasticsearch_commit": "$ELASTIC_COMMIT_HASH", - "created_at": "`date`", - "timestamp": "`FORCE_COLOR=0 node -p 'Date.now()'`" -} -EOT - - gsutil -h "Cache-Control:no-cache, max-age=0, no-transform" \ - cp $MANIFEST_FILE_NAME "gs://$ES_SERVERLESS_BUCKET/$MANIFEST_FILE_NAME" - gsutil acl ch -u AllUsers:R "gs://$ES_SERVERLESS_BUCKET/$MANIFEST_FILE_NAME" - MANIFEST_UPLOAD_PATH="$MANIFEST_FILE_NAME" - -elif [[ "${PUBLISH_MANIFEST:-}" =~ ^(1|true)$ ]]; then - echo "--- Skipping upload of latest-verified manifest to GCS, ES Serverless build tag is not pointing to a hash" -elif [[ "$SOURCE_IMAGE_OR_TAG" =~ ^git-[0-9a-fA-F]{12}$ ]]; then - echo "--- Skipping upload of latest-verified manifest to GCS, flag was not provided" -else - echo "--- Skipping upload of latest-verified manifest to GCS, no flag and hash provided" -fi - echo "--- Annotating build with info" cat << EOT | buildkite-agent annotate --style "success"

Promotion successful!

@@ -104,5 +71,4 @@ cat << EOT | buildkite-agent annotate --style "success"
Source image: $SOURCE_IMAGE
Kibana commit: $BUILDKITE_COMMIT
Elasticsearch commit: $ELASTIC_COMMIT_HASH -
Manifest file: $MANIFEST_UPLOAD_PATH EOT diff --git a/.buildkite/scripts/steps/es_snapshots/create_manifest.ts b/.buildkite/scripts/steps/es_snapshots/create_manifest.ts index 659a034d793bc..c2a3250c1e853 100644 --- a/.buildkite/scripts/steps/es_snapshots/create_manifest.ts +++ b/.buildkite/scripts/steps/es_snapshots/create_manifest.ts @@ -103,6 +103,7 @@ interface ManifestEntry { set -euo pipefail echo '--- Upload files to GCS' + .buildkite/scripts/common/activate_service_account.sh ${BASE_BUCKET_DAILY} cd "${destination}" gsutil -m cp -r *.* gs://${BASE_BUCKET_DAILY}/${DESTINATION} cp manifest.json manifest-latest.json diff --git a/.buildkite/scripts/steps/es_snapshots/promote_manifest.ts b/.buildkite/scripts/steps/es_snapshots/promote_manifest.ts index 7b0f9b7a319a8..3d4009139e3dc 100644 --- a/.buildkite/scripts/steps/es_snapshots/promote_manifest.ts +++ b/.buildkite/scripts/steps/es_snapshots/promote_manifest.ts @@ -38,6 +38,7 @@ import { BASE_BUCKET_DAILY, BASE_BUCKET_PERMANENT } from './bucket_config'; execSync( ` set -euo pipefail + .buildkite/scripts/common/activate_service_account.sh ${bucket} cp manifest.json manifest-latest-verified.json gsutil -h "Cache-Control:no-cache, max-age=0, no-transform" cp manifest-latest-verified.json gs://${BASE_BUCKET_DAILY}/${version}/ rm manifest.json diff --git a/.buildkite/scripts/steps/fips/build.sh b/.buildkite/scripts/steps/fips/build.sh new file mode 100644 index 0000000000000..dcd3cc3b0bb2f --- /dev/null +++ b/.buildkite/scripts/steps/fips/build.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +set -euo pipefail + +.buildkite/scripts/bootstrap.sh + +source .buildkite/scripts/common/util.sh +source .buildkite/scripts/steps/artifacts/env.sh + +echo "$KIBANA_DOCKER_PASSWORD" | docker login -u "$KIBANA_DOCKER_USERNAME" --password-stdin docker.elastic.co +mkdir -p target +download_artifact "kibana-$FULL_VERSION-linux-x86_64.tar.gz" ./target --build "${KIBANA_BUILD_ID:-$BUILDKITE_BUILD_ID}" + +echo "--- Build FIPS image" +node scripts/build \ + --skip-initialize \ + --skip-generic-folders \ + --skip-platform-folders \ + --skip-cdn-assets \ + --skip-archives \ + --docker-images \ + --docker-namespace="kibana-ci" \ + --docker-tag-qualifier="$BUILDKITE_COMMIT" \ + --docker-push \ + --skip-docker-ubi \ + --skip-docker-ubuntu \ + --skip-docker-cloud \ + --skip-docker-serverless \ + --skip-docker-contexts + +docker logout docker.elastic.co + +# Moving to `target/` first will keep `buildkite-agent` from including directories in the artifact name +cd "$KIBANA_DIR/target" +buildkite-agent artifact upload "./*docker-image*.tar.gz" diff --git a/.buildkite/scripts/steps/functional/scalability_dataset_extraction.sh b/.buildkite/scripts/steps/functional/scalability_dataset_extraction.sh index d22e9890b2de5..ebf9e28d2c9ea 100755 --- a/.buildkite/scripts/steps/functional/scalability_dataset_extraction.sh +++ b/.buildkite/scripts/steps/functional/scalability_dataset_extraction.sh @@ -41,6 +41,9 @@ download_artifact kibana-default-plugins.tar.gz "${OUTPUT_DIR}/" --build "${KIBA echo "--- Adding commit info" echo "${BUILDKITE_COMMIT}" > "${OUTPUT_DIR}/KIBANA_COMMIT_HASH" +echo "--- Activating service-account for gsutil to access gs://kibana-performance" +.buildkite/scripts/common/activate_service_account.sh gs://kibana-performance + echo "--- Uploading ${OUTPUT_REL} dir to ${GCS_BUCKET}" cd "${OUTPUT_DIR}/.." gsutil -m cp -r "${BUILD_ID}" "${GCS_BUCKET}" diff --git a/.buildkite/scripts/steps/package_testing/build.sh b/.buildkite/scripts/steps/package_testing/build.sh index 892b8bf312d51..9cd03ac82ac0f 100755 --- a/.buildkite/scripts/steps/package_testing/build.sh +++ b/.buildkite/scripts/steps/package_testing/build.sh @@ -4,7 +4,7 @@ set -euo pipefail .buildkite/scripts/bootstrap.sh -node scripts/build --all-platforms --debug --skip-docker-cloud --skip-docker-serverless --skip-docker-ubi --skip-docker-contexts --skip-cdn-assets +node scripts/build --all-platforms --debug --skip-docker-cloud --skip-docker-serverless --skip-docker-ubi --skip-docker-fips --skip-docker-contexts --skip-cdn-assets DOCKER_FILE="kibana-$KIBANA_PKG_VERSION-SNAPSHOT-docker-image.tar.gz" diff --git a/.buildkite/scripts/steps/scalability/benchmarking.sh b/.buildkite/scripts/steps/scalability/benchmarking.sh index 39acf4203af37..79875fb04d5fe 100755 --- a/.buildkite/scripts/steps/scalability/benchmarking.sh +++ b/.buildkite/scripts/steps/scalability/benchmarking.sh @@ -19,6 +19,9 @@ rm -rf "${KIBANA_LOAD_TESTING_DIR}" rm -rf "${GCS_ARTIFACTS_DIR}" download_artifacts() { + echo Activating service-account for gsutil to access gs://kibana-performance + .buildkite/scripts/common/activate_service_account.sh gs://kibana-performance + mkdir -p "${GCS_ARTIFACTS_DIR}" gsutil cp "$GCS_BUCKET/latest" "${GCS_ARTIFACTS_DIR}/" diff --git a/.buildkite/scripts/steps/storybooks/build_and_upload.ts b/.buildkite/scripts/steps/storybooks/build_and_upload.ts index 4ea59b4a8298d..e7545f5011df1 100644 --- a/.buildkite/scripts/steps/storybooks/build_and_upload.ts +++ b/.buildkite/scripts/steps/storybooks/build_and_upload.ts @@ -9,6 +9,7 @@ import { execSync } from 'child_process'; import fs from 'fs'; import path from 'path'; +import { getKibanaDir } from '#pipeline-utils'; // TODO - how to generate this dynamically? const STORYBOOKS = [ @@ -117,7 +118,12 @@ const upload = () => { fs.writeFileSync('index.html', html); console.log('--- Uploading Storybooks'); + const activateScript = path.relative( + process.cwd(), + path.join(getKibanaDir(), '.buildkite', 'scripts', 'common', 'activate_service_account.sh') + ); exec(` + ${activateScript} gs://ci-artifacts.kibana.dev gsutil -q -m cp -r -z js,css,html,json,map,txt,svg '*' 'gs://${STORYBOOK_BUCKET}/${STORYBOOK_DIRECTORY}/${process.env.BUILDKITE_COMMIT}/' gsutil -h "Cache-Control:no-cache, max-age=0, no-transform" cp -z html 'index.html' 'gs://${STORYBOOK_BUCKET}/${STORYBOOK_DIRECTORY}/latest/' `); diff --git a/.buildkite/scripts/steps/webpack_bundle_analyzer/upload.ts b/.buildkite/scripts/steps/webpack_bundle_analyzer/upload.ts index 73c24a82be6df..7bb45990bd467 100644 --- a/.buildkite/scripts/steps/webpack_bundle_analyzer/upload.ts +++ b/.buildkite/scripts/steps/webpack_bundle_analyzer/upload.ts @@ -9,6 +9,7 @@ import { execSync } from 'child_process'; import fs from 'fs'; import path from 'path'; +import { getKibanaDir } from '#pipeline-utils'; const GITHUB_CONTEXT = 'Build and Publish Webpack bundle analyzer reports'; @@ -54,7 +55,12 @@ const upload = () => { fs.writeFileSync('index.html', html); console.log('--- Uploading Webpack Bundle Analyzer reports'); + const activateScript = path.relative( + process.cwd(), + path.join(getKibanaDir(), '.buildkite', 'scripts', 'common', 'activate_service_account.sh') + ); exec(` + ${activateScript} gs://ci-artifacts.kibana.dev gsutil -q -m cp -r -z html '*' 'gs://${WEBPACK_REPORTS_BUCKET}/${WEBPACK_REPORTS}/${process.env.BUILDKITE_COMMIT}/' gsutil -h "Cache-Control:no-cache, max-age=0, no-transform" cp -z html 'index.html' 'gs://${WEBPACK_REPORTS_BUCKET}/${WEBPACK_REPORTS}/latest/' `); diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 02837458e3046..dcfce13388c7a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -324,6 +324,7 @@ src/plugins/data_view_editor @elastic/kibana-data-discovery examples/data_view_field_editor_example @elastic/kibana-data-discovery src/plugins/data_view_field_editor @elastic/kibana-data-discovery src/plugins/data_view_management @elastic/kibana-data-discovery +packages/kbn-data-view-utils @elastic/kibana-data-discovery src/plugins/data_views @elastic/kibana-data-discovery x-pack/plugins/data_visualizer @elastic/ml-ui x-pack/plugins/dataset_quality @elastic/obs-ux-logs-team @@ -353,8 +354,8 @@ packages/kbn-docs-utils @elastic/kibana-operations packages/kbn-dom-drag-drop @elastic/kibana-visualizations @elastic/kibana-data-discovery packages/kbn-ebt-tools @elastic/kibana-core packages/kbn-ecs @elastic/kibana-core @elastic/security-threat-hunting-investigations -x-pack/packages/security-solution/ecs_data_quality_dashboard @elastic/security-threat-hunting-investigations -x-pack/plugins/ecs_data_quality_dashboard @elastic/security-threat-hunting-investigations +x-pack/packages/security-solution/ecs_data_quality_dashboard @elastic/security-threat-hunting-explore +x-pack/plugins/ecs_data_quality_dashboard @elastic/security-threat-hunting-explore packages/kbn-elastic-agent-utils @elastic/obs-ux-logs-team x-pack/packages/kbn-elastic-assistant @elastic/security-generative-ai x-pack/packages/kbn-elastic-assistant-common @elastic/security-generative-ai @@ -1500,6 +1501,9 @@ x-pack/plugins/security_solution/public/flyout/entity_details @elastic/security- x-pack/plugins/security_solution/common/api/entity_analytics @elastic/security-entity-analytics x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score @elastic/security-entity-analytics +## Security Solution sub teams - GenAI +x-pack/test/security_solution_api_integration/test_suites/genai @elastic/security-generative-ai + # Security Defend Workflows - OSQuery Ownership /x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions @elastic/security-defend-workflows /x-pack/plugins/security_solution/public/detection_engine/rule_response_actions @elastic/security-defend-workflows diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index dc6e3424f37cc..2e5a688aadf5c 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-02-06 +date: 2024-02-08 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 b136669a82af8..c4f09f5856a44 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/ai_assistant_management_observability.mdx b/api_docs/ai_assistant_management_observability.mdx index ecbe647883161..fa3a76f742ff2 100644 --- a/api_docs/ai_assistant_management_observability.mdx +++ b/api_docs/ai_assistant_management_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiAssistantManagementObservability title: "aiAssistantManagementObservability" image: https://source.unsplash.com/400x175/?github description: API docs for the aiAssistantManagementObservability plugin -date: 2024-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiAssistantManagementObservability'] --- import aiAssistantManagementObservabilityObj from './ai_assistant_management_observability.devdocs.json'; diff --git a/api_docs/ai_assistant_management_selection.mdx b/api_docs/ai_assistant_management_selection.mdx index 9734e0ef04533..f03427a765ea1 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiAssistantManagementSelection'] --- import aiAssistantManagementSelectionObj from './ai_assistant_management_selection.devdocs.json'; diff --git a/api_docs/aiops.devdocs.json b/api_docs/aiops.devdocs.json index 6a4ad90613be7..fc291bd721831 100644 --- a/api_docs/aiops.devdocs.json +++ b/api_docs/aiops.devdocs.json @@ -1142,22 +1142,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "aiops", - "id": "def-public.LogRateAnalysisContentWrapperProps.setGlobalState", - "type": "Any", - "tags": [], - "label": "setGlobalState", - "description": [ - "On global timefilter update" - ], - "signature": [ - "any" - ], - "path": "x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content_wrapper.tsx", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "aiops", "id": "def-public.LogRateAnalysisContentWrapperProps.initialAnalysisStart", diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 4dc47efaddccf..a2ecdca05e508 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) for questi | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 70 | 1 | 4 | 1 | +| 69 | 0 | 4 | 1 | ## Client diff --git a/api_docs/alerting.devdocs.json b/api_docs/alerting.devdocs.json index 83be25a7e9802..2d02c951d2e79 100644 --- a/api_docs/alerting.devdocs.json +++ b/api_docs/alerting.devdocs.json @@ -3146,10 +3146,10 @@ }, { "parentPluginId": "alerting", - "id": "def-server.Rule.notificationDelay", + "id": "def-server.Rule.alertDelay", "type": "Object", "tags": [], - "label": "notificationDelay", + "label": "alertDelay", "description": [], "signature": [ "Readonly<{} & { active: number; }> | undefined" @@ -3257,7 +3257,7 @@ "section": "def-common.SanitizedRule", "text": "SanitizedRule" }, - ", \"id\" | \"consumer\" | \"name\" | \"actions\" | \"tags\" | \"enabled\" | \"schedule\" | \"createdBy\" | \"updatedBy\" | \"createdAt\" | \"updatedAt\" | \"throttle\" | \"muteAll\" | \"notifyWhen\" | \"snoozeSchedule\" | \"revision\"> & { producer: string; ruleTypeId: string; ruleTypeName: string; }" + ", \"id\" | \"consumer\" | \"name\" | \"actions\" | \"tags\" | \"enabled\" | \"schedule\" | \"createdBy\" | \"updatedBy\" | \"createdAt\" | \"updatedAt\" | \"throttle\" | \"muteAll\" | \"notifyWhen\" | \"snoozeSchedule\" | \"revision\" | \"alertDelay\"> & { producer: string; ruleTypeId: string; ruleTypeName: string; }" ], "path": "x-pack/plugins/alerting/server/types.ts", "deprecated": false, @@ -6314,6 +6314,48 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.AlertDelay", + "type": "Interface", + "tags": [], + "label": "AlertDelay", + "description": [], + "signature": [ + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.AlertDelay", + "text": "AlertDelay" + }, + " extends ", + { + "pluginId": "@kbn/core-saved-objects-common", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", + "section": "def-common.SavedObjectAttributes", + "text": "SavedObjectAttributes" + } + ], + "path": "x-pack/plugins/alerting/common/rule.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-common.AlertDelay.active", + "type": "number", + "tags": [], + "label": "active", + "description": [], + "path": "x-pack/plugins/alerting/common/rule.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.AlertingFrameworkHealth", @@ -7845,31 +7887,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "alerting", - "id": "def-common.NotificationDelay", - "type": "Interface", - "tags": [], - "label": "NotificationDelay", - "description": [], - "path": "x-pack/plugins/alerting/common/rule.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "alerting", - "id": "def-common.NotificationDelay.active", - "type": "number", - "tags": [], - "label": "active", - "description": [], - "path": "x-pack/plugins/alerting/common/rule.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "alerting", "id": "def-common.Rule", @@ -8367,18 +8384,18 @@ }, { "parentPluginId": "alerting", - "id": "def-common.Rule.notificationDelay", + "id": "def-common.Rule.alertDelay", "type": "Object", "tags": [], - "label": "notificationDelay", + "label": "alertDelay", "description": [], "signature": [ { "pluginId": "alerting", "scope": "common", "docId": "kibAlertingPluginApi", - "section": "def-common.NotificationDelay", - "text": "NotificationDelay" + "section": "def-common.AlertDelay", + "text": "AlertDelay" }, " | undefined" ], @@ -11546,7 +11563,7 @@ "section": "def-common.SanitizedRule", "text": "SanitizedRule" }, - ", \"id\" | \"consumer\" | \"name\" | \"actions\" | \"tags\" | \"enabled\" | \"schedule\" | \"createdBy\" | \"updatedBy\" | \"createdAt\" | \"updatedAt\" | \"throttle\" | \"muteAll\" | \"notifyWhen\" | \"snoozeSchedule\" | \"revision\"> & { producer: string; ruleTypeId: string; ruleTypeName: string; }" + ", \"id\" | \"consumer\" | \"name\" | \"actions\" | \"tags\" | \"enabled\" | \"schedule\" | \"createdBy\" | \"updatedBy\" | \"createdAt\" | \"updatedAt\" | \"throttle\" | \"muteAll\" | \"notifyWhen\" | \"snoozeSchedule\" | \"revision\" | \"alertDelay\"> & { producer: string; ruleTypeId: string; ruleTypeName: string; }" ], "path": "x-pack/plugins/alerting/common/rule.ts", "deprecated": false, @@ -11561,7 +11578,7 @@ "label": "TrackedLifecycleAlertState", "description": [], "signature": [ - "{ alertId: string; alertUuid: string; started: string; flappingHistory: boolean[]; flapping: boolean; pendingRecoveredCount: number; }" + "{ alertId: string; alertUuid: string; started: string; flappingHistory: boolean[]; flapping: boolean; pendingRecoveredCount: number; activeCount: number; }" ], "path": "x-pack/packages/kbn-alerting-state-types/src/lifecycle_state.ts", "deprecated": false, @@ -11601,7 +11618,7 @@ "\nThis is redefined instead of derived from above `wrappedStateRt` because\nthere's no easy way to instantiate generic values such as the runtime type\nfactory function." ], "signature": [ - "RuleTypeState & { wrapped: State; trackedAlerts: Record; trackedAlertsRecovered: Record; }" + "RuleTypeState & { wrapped: State; trackedAlerts: Record; trackedAlertsRecovered: Record; }" ], "path": "x-pack/packages/kbn-alerting-state-types/src/lifecycle_state.ts", "deprecated": false, diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index b28b99e61bf93..a7e39b0784003 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-02-06 +date: 2024-02-08 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 489d0831b12ab..cb88e4b523e3c 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-02-06 +date: 2024-02-08 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 e7db9e67d50b4..0db7c0441306c 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apmDataAccess'] --- import apmDataAccessObj from './apm_data_access.devdocs.json'; diff --git a/api_docs/asset_manager.mdx b/api_docs/asset_manager.mdx index e0faf9044afc3..72dd300e3f6ab 100644 --- a/api_docs/asset_manager.mdx +++ b/api_docs/asset_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetManager title: "assetManager" image: https://source.unsplash.com/400x175/?github description: API docs for the assetManager plugin -date: 2024-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetManager'] --- import assetManagerObj from './asset_manager.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 99044ae268660..6e679de83bfc2 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-02-06 +date: 2024-02-08 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 9797b1c42c5ab..b1eff88214d4e 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-02-06 +date: 2024-02-08 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 810c371e4fdda..6c8a7b48a899f 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] --- import canvasObj from './canvas.devdocs.json'; diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index 7c6cfa724b40f..b0b4dac09d20f 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-02-06 +date: 2024-02-08 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 a3415b68c5978..6155cfbba8614 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] --- import chartsObj from './charts.devdocs.json'; diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index dd9afabb3bb4b..d141f83ff45a9 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index ccb04eb451052..55a52449f1718 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-02-06 +date: 2024-02-08 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 684e79492bc57..93a43658a51fe 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDefend'] --- import cloudDefendObj from './cloud_defend.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index 4b64af843ea22..5915c7ca85ded 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2024-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 54183ea0b6c15..3114799838b01 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-02-06 +date: 2024-02-08 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 6e3708689ad7d..527e60940783b 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-02-06 +date: 2024-02-08 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 76fdc01c4dbc0..0588bc2a0bf97 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-02-06 +date: 2024-02-08 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 c2f3050142d11..926346635e430 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-02-06 +date: 2024-02-08 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 1092617fee813..8e5c36fbc0747 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-02-06 +date: 2024-02-08 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 bf4b2ad0a570b..7d5bedf0444df 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-02-06 +date: 2024-02-08 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 5f21dd789ddbf..791ceaa540a1d 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.devdocs.json b/api_docs/data.devdocs.json index 58d3d9fbf9ceb..48315db6ab3eb 100644 --- a/api_docs/data.devdocs.json +++ b/api_docs/data.devdocs.json @@ -17339,6 +17339,20 @@ "path": "src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "data", + "id": "def-server.FieldDescriptor.defaultFormatter", + "type": "string", + "tags": [], + "label": "defaultFormatter", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -20030,7 +20044,7 @@ }, " | undefined; fixedInterval?: string[] | undefined; timeZone?: string[] | undefined; timeSeriesDimension?: boolean | undefined; timeSeriesMetric?: ", "MappingTimeSeriesMetricType", - " | undefined; shortDotsEnable?: boolean | undefined; isMapped?: boolean | undefined; parentName?: string | undefined; }" + " | undefined; shortDotsEnable?: boolean | undefined; isMapped?: boolean | undefined; parentName?: string | undefined; defaultFormatter?: string | undefined; }" ], "path": "src/plugins/data_views/common/fields/data_view_field.ts", "deprecated": false, @@ -20106,6 +20120,20 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "data", + "id": "def-common.DataViewField.defaultFormatter", + "type": "string", + "tags": [], + "label": "defaultFormatter", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/data_views/common/fields/data_view_field.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "data", "id": "def-common.DataViewField.runtimeField", @@ -20693,7 +20721,7 @@ "section": "def-common.IFieldSubType", "text": "IFieldSubType" }, - " | undefined; customLabel: string | undefined; }" + " | undefined; customLabel: string | undefined; defaultFormatter: string | undefined; }" ], "path": "src/plugins/data_views/common/fields/data_view_field.ts", "deprecated": false, @@ -25174,7 +25202,7 @@ }, " | undefined; fixedInterval?: string[] | undefined; timeZone?: string[] | undefined; timeSeriesDimension?: boolean | undefined; timeSeriesMetric?: ", "MappingTimeSeriesMetricType", - " | undefined; shortDotsEnable?: boolean | undefined; isMapped?: boolean | undefined; parentName?: string | undefined; }" + " | undefined; shortDotsEnable?: boolean | undefined; isMapped?: boolean | undefined; parentName?: string | undefined; defaultFormatter?: string | undefined; }" ], "path": "src/plugins/data_views/common/types.ts", "deprecated": false, diff --git a/api_docs/data.mdx b/api_docs/data.mdx index 02f2f9a45d5af..0266dc6c63480 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3235 | 31 | 2583 | 23 | +| 3237 | 31 | 2585 | 23 | ## Client diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index d4aaa572a307e..6d57ee43cdb19 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3235 | 31 | 2583 | 23 | +| 3237 | 31 | 2585 | 23 | ## Client diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 690cf4b267020..6cf0ec8b332ee 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3235 | 31 | 2583 | 23 | +| 3237 | 31 | 2585 | 23 | ## Client diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 4ab7ddd937589..a986ac73a1a4a 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-02-06 +date: 2024-02-08 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 e55b392d2aa29..b51cccc090336 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-02-06 +date: 2024-02-08 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 cf830bb4bee2d..2448185780faf 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.devdocs.json b/api_docs/data_views.devdocs.json index 4686208acd12e..b8995d9fca912 100644 --- a/api_docs/data_views.devdocs.json +++ b/api_docs/data_views.devdocs.json @@ -1045,7 +1045,7 @@ }, " | undefined; fixedInterval?: string[] | undefined; timeZone?: string[] | undefined; timeSeriesDimension?: boolean | undefined; timeSeriesMetric?: ", "MappingTimeSeriesMetricType", - " | undefined; shortDotsEnable?: boolean | undefined; isMapped?: boolean | undefined; parentName?: string | undefined; }" + " | undefined; shortDotsEnable?: boolean | undefined; isMapped?: boolean | undefined; parentName?: string | undefined; defaultFormatter?: string | undefined; }" ], "path": "src/plugins/data_views/common/fields/data_view_field.ts", "deprecated": false, @@ -1121,6 +1121,20 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewField.defaultFormatter", + "type": "string", + "tags": [], + "label": "defaultFormatter", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/data_views/common/fields/data_view_field.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "dataViews", "id": "def-public.DataViewField.runtimeField", @@ -1708,7 +1722,7 @@ "section": "def-common.IFieldSubType", "text": "IFieldSubType" }, - " | undefined; customLabel: string | undefined; }" + " | undefined; customLabel: string | undefined; defaultFormatter: string | undefined; }" ], "path": "src/plugins/data_views/common/fields/data_view_field.ts", "deprecated": false, @@ -6327,7 +6341,7 @@ }, " | undefined; fixedInterval?: string[] | undefined; timeZone?: string[] | undefined; timeSeriesDimension?: boolean | undefined; timeSeriesMetric?: ", "MappingTimeSeriesMetricType", - " | undefined; shortDotsEnable?: boolean | undefined; isMapped?: boolean | undefined; parentName?: string | undefined; }" + " | undefined; shortDotsEnable?: boolean | undefined; isMapped?: boolean | undefined; parentName?: string | undefined; defaultFormatter?: string | undefined; }" ], "path": "src/plugins/data_views/common/types.ts", "deprecated": false, @@ -10251,6 +10265,20 @@ "path": "src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "dataViews", + "id": "def-server.FieldDescriptor.defaultFormatter", + "type": "string", + "tags": [], + "label": "defaultFormatter", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -10385,7 +10413,7 @@ }, " | undefined; fixedInterval?: string[] | undefined; timeZone?: string[] | undefined; timeSeriesDimension?: boolean | undefined; timeSeriesMetric?: ", "MappingTimeSeriesMetricType", - " | undefined; shortDotsEnable?: boolean | undefined; isMapped?: boolean | undefined; parentName?: string | undefined; }" + " | undefined; shortDotsEnable?: boolean | undefined; isMapped?: boolean | undefined; parentName?: string | undefined; defaultFormatter?: string | undefined; }" ], "path": "src/plugins/data_views/common/types.ts", "deprecated": false, @@ -13069,7 +13097,7 @@ }, " | undefined; fixedInterval?: string[] | undefined; timeZone?: string[] | undefined; timeSeriesDimension?: boolean | undefined; timeSeriesMetric?: ", "MappingTimeSeriesMetricType", - " | undefined; shortDotsEnable?: boolean | undefined; isMapped?: boolean | undefined; parentName?: string | undefined; }" + " | undefined; shortDotsEnable?: boolean | undefined; isMapped?: boolean | undefined; parentName?: string | undefined; defaultFormatter?: string | undefined; }" ], "path": "src/plugins/data_views/common/fields/data_view_field.ts", "deprecated": false, @@ -13145,6 +13173,20 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "dataViews", + "id": "def-common.DataViewField.defaultFormatter", + "type": "string", + "tags": [], + "label": "defaultFormatter", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/data_views/common/fields/data_view_field.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "dataViews", "id": "def-common.DataViewField.runtimeField", @@ -13732,7 +13774,7 @@ "section": "def-common.IFieldSubType", "text": "IFieldSubType" }, - " | undefined; customLabel: string | undefined; }" + " | undefined; customLabel: string | undefined; defaultFormatter: string | undefined; }" ], "path": "src/plugins/data_views/common/fields/data_view_field.ts", "deprecated": false, @@ -20951,7 +20993,7 @@ }, " | undefined; fixedInterval?: string[] | undefined; timeZone?: string[] | undefined; timeSeriesDimension?: boolean | undefined; timeSeriesMetric?: ", "MappingTimeSeriesMetricType", - " | undefined; shortDotsEnable?: boolean | undefined; isMapped?: boolean | undefined; parentName?: string | undefined; }" + " | undefined; shortDotsEnable?: boolean | undefined; isMapped?: boolean | undefined; parentName?: string | undefined; defaultFormatter?: string | undefined; }" ], "path": "src/plugins/data_views/common/types.ts", "deprecated": false, diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index d6ae15964c37b..84e6eae5ffd18 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] --- import dataViewsObj from './data_views.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 | |-------------------|-----------|------------------------|-----------------| -| 940 | 0 | 273 | 4 | +| 943 | 0 | 276 | 4 | ## Client diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index cbc54a87d05f7..b09f9dd8a0a27 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-02-06 +date: 2024-02-08 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 d0fbeaa3f078c..27f53e5af8347 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-02-06 +date: 2024-02-08 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 f43faceb9dc22..a433ea47849f4 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 53e0429bb5bef..3d5d34b7d900c 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index a7977ec321b47..e41eae851ed2d 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 86251f2a1bba9..359ab0e7288e9 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index 5079b784fd1ce..962b6c9db5e41 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] --- import discoverObj from './discover.devdocs.json'; diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index fb49a8f059b34..db0763d64fdb3 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index 3d5558a3de2a2..0a9d3737f22cd 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,14 +8,14 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2024-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; APIs used to assess the quality of data in Elasticsearch indexes -Contact [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) for questions regarding this plugin. +Contact [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) for questions regarding this plugin. **Code health stats** diff --git a/api_docs/elastic_assistant.mdx b/api_docs/elastic_assistant.mdx index da09fc3f341eb..62ab029aa3e2f 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'elasticAssistant'] --- import elasticAssistantObj from './elastic_assistant.devdocs.json'; diff --git a/api_docs/embeddable.devdocs.json b/api_docs/embeddable.devdocs.json index cbd3e011180d6..fc380952c04d6 100644 --- a/api_docs/embeddable.devdocs.json +++ b/api_docs/embeddable.devdocs.json @@ -720,6 +720,52 @@ ], "returnComment": [] }, + { + "parentPluginId": "embeddable", + "id": "def-public.Container.addNewPanel", + "type": "Function", + "tags": [], + "label": "addNewPanel", + "description": [], + "signature": [ + "(panelPackage: ", + { + "pluginId": "@kbn/presentation-containers", + "scope": "common", + "docId": "kibKbnPresentationContainersPluginApi", + "section": "def-common.PanelPackage", + "text": "PanelPackage" + }, + ") => Promise" + ], + "path": "src/plugins/embeddable/public/lib/containers/container.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "embeddable", + "id": "def-public.Container.addNewPanel.$1", + "type": "Object", + "tags": [], + "label": "panelPackage", + "description": [], + "signature": [ + { + "pluginId": "@kbn/presentation-containers", + "scope": "common", + "docId": "kibKbnPresentationContainersPluginApi", + "section": "def-common.PanelPackage", + "text": "PanelPackage" + } + ], + "path": "src/plugins/embeddable/public/lib/containers/container.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, { "parentPluginId": "embeddable", "id": "def-public.Container.replacePanel", @@ -2557,7 +2603,15 @@ "section": "def-common.PublishesLastSavedState", "text": "PublishesLastSavedState" }, - " & { registerPanelApi: (panelId: string, panelApi: ApiType) => void; removePanel: (panelId: string) => void; canRemovePanels?: (() => boolean) | undefined; replacePanel: (idToRemove: string, newPanel: ", + " & { addNewPanel: (panel: ", + { + "pluginId": "@kbn/presentation-containers", + "scope": "common", + "docId": "kibKbnPresentationContainersPluginApi", + "section": "def-common.PanelPackage", + "text": "PanelPackage" + }, + ", displaySuccessMessage?: boolean | undefined) => Promise; registerPanelApi: (panelId: string, panelApi: ApiType) => void; removePanel: (panelId: string) => void; canRemovePanels?: (() => boolean) | undefined; replacePanel: (idToRemove: string, newPanel: ", { "pluginId": "@kbn/presentation-containers", "scope": "common", @@ -8970,6 +9024,54 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "embeddable", + "id": "def-public.registerSavedObjectToPanelMethod", + "type": "Function", + "tags": [], + "label": "registerSavedObjectToPanelMethod", + "description": [], + "signature": [ + "(savedObjectType: string, method: SavedObjectToPanelMethod) => void" + ], + "path": "src/plugins/embeddable/public/registry/saved_object_to_panel_methods.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "embeddable", + "id": "def-public.registerSavedObjectToPanelMethod.$1", + "type": "string", + "tags": [], + "label": "savedObjectType", + "description": [], + "signature": [ + "string" + ], + "path": "src/plugins/embeddable/public/registry/saved_object_to_panel_methods.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "embeddable", + "id": "def-public.registerSavedObjectToPanelMethod.$2", + "type": "Function", + "tags": [], + "label": "method", + "description": [], + "signature": [ + "SavedObjectToPanelMethod" + ], + "path": "src/plugins/embeddable/public/registry/saved_object_to_panel_methods.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "embeddable", "id": "def-public.runEmbeddableFactoryMigrations", @@ -13404,7 +13506,15 @@ "section": "def-common.PublishesLastSavedState", "text": "PublishesLastSavedState" }, - " & { registerPanelApi: (panelId: string, panelApi: ApiType) => void; removePanel: (panelId: string) => void; canRemovePanels?: (() => boolean) | undefined; replacePanel: (idToRemove: string, newPanel: ", + " & { addNewPanel: (panel: ", + { + "pluginId": "@kbn/presentation-containers", + "scope": "common", + "docId": "kibKbnPresentationContainersPluginApi", + "section": "def-common.PanelPackage", + "text": "PanelPackage" + }, + ", displaySuccessMessage?: boolean | undefined) => Promise; registerPanelApi: (panelId: string, panelApi: ApiType) => void; removePanel: (panelId: string) => void; canRemovePanels?: (() => boolean) | undefined; replacePanel: (idToRemove: string, newPanel: ", { "pluginId": "@kbn/presentation-containers", "scope": "common", diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 24b016ee2226c..e4757ac0fa026 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddable'] --- import embeddableObj from './embeddable.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kib | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 557 | 1 | 452 | 8 | +| 562 | 1 | 457 | 8 | ## Client diff --git a/api_docs/embeddable_enhanced.mdx b/api_docs/embeddable_enhanced.mdx index 2217536426156..edacdc9d052fa 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-02-06 +date: 2024-02-08 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 6cce0344d74fb..53d150a26ef56 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-02-06 +date: 2024-02-08 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 9f40f00bd4d9f..31ce75f352a2b 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index 431f364c71549..cebbea3aa42b1 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 685b7d9977123..cc9070788f35c 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-02-06 +date: 2024-02-08 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 27147e025d02d..059381734ebd4 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-02-06 +date: 2024-02-08 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 0222d568741dc..bc0599a12a058 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-02-06 +date: 2024-02-08 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 39d84d6ef6c50..0c5e1b26620d6 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-02-06 +date: 2024-02-08 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 a1b91726cfda3..910ad5fcb79dc 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-02-06 +date: 2024-02-08 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 a257739e8ba20..6c9ac5adda786 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-02-06 +date: 2024-02-08 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 23c6c06b90614..192bc924a0157 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-02-06 +date: 2024-02-08 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 6f73940c722fb..b24dc2180c5ff 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-02-06 +date: 2024-02-08 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 056436375c2f9..847888ad3d734 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-02-06 +date: 2024-02-08 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 abc5b5e20f068..05e169cdacb4f 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-02-06 +date: 2024-02-08 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 4bb55dd1267f8..b84b768bd7765 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-02-06 +date: 2024-02-08 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 a488cb4559538..e9741fccf718a 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-02-06 +date: 2024-02-08 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 788eff9d3c910..ef330144a3107 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-02-06 +date: 2024-02-08 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 a9e70cdb16a21..e29d8c3310626 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-02-06 +date: 2024-02-08 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 593425fe77909..951cb74b24a4b 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-02-06 +date: 2024-02-08 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 3b3b71384b4bc..f8dd39ea344b2 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-02-06 +date: 2024-02-08 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 b876e79d5052b..6a8c5d756baeb 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-02-06 +date: 2024-02-08 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 214de7d3869a6..56fd7d9a3e9e2 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-02-06 +date: 2024-02-08 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 90f6f86f6955d..cce1d5fed656e 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-02-06 +date: 2024-02-08 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 6c9cafe2c18cc..671764bec61f0 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index 76db797294cf9..836a66f011251 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-02-06 +date: 2024-02-08 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 d5623f1296f86..51c67ed1e825d 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-02-06 +date: 2024-02-08 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 bc92e6c466c6b..9b30702e06533 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-02-06 +date: 2024-02-08 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 cd548cf1b7362..de83a55e605a0 100644 --- a/api_docs/fleet.devdocs.json +++ b/api_docs/fleet.devdocs.json @@ -7065,6 +7065,49 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "fleet", + "id": "def-server.ArtifactsClientInterface.fetchAll", + "type": "Function", + "tags": [], + "label": "fetchAll", + "description": [], + "signature": [ + "(options?: ", + "FetchAllArtifactsOptions", + " | undefined) => AsyncIterable<", + { + "pluginId": "fleet", + "scope": "server", + "docId": "kibFleetPluginApi", + "section": "def-server.Artifact", + "text": "Artifact" + }, + "[]>" + ], + "path": "x-pack/plugins/fleet/server/services/artifacts/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "fleet", + "id": "def-server.ArtifactsClientInterface.fetchAll.$1", + "type": "CompoundType", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "FetchAllArtifactsOptions", + " | undefined" + ], + "path": "x-pack/plugins/fleet/server/services/artifacts/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] } ], "initialIsOpen": false @@ -11299,6 +11342,146 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.fetchAllItemIds", + "type": "Function", + "tags": [], + "label": "fetchAllItemIds", + "description": [ + "\nReturns an `AsyncIterable` for retrieving all integration policy IDs" + ], + "signature": [ + "(soClient: ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + }, + ", options?: ", + "PackagePolicyClientFetchAllItemIdsOptions", + " | undefined) => AsyncIterable" + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.fetchAllItemIds.$1", + "type": "Object", + "tags": [], + "label": "soClient", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + } + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.fetchAllItemIds.$2", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "PackagePolicyClientFetchAllItemIdsOptions", + " | undefined" + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.fetchAllItems", + "type": "Function", + "tags": [], + "label": "fetchAllItems", + "description": [ + "\nReturns an `AsyncIterable` for retrieving all integration policies" + ], + "signature": [ + "(soClient: ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + }, + ", options?: ", + "PackagePolicyClientFetchAllItemsOptions", + " | undefined) => AsyncIterable<", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.PackagePolicy", + "text": "PackagePolicy" + }, + "[]>" + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.fetchAllItems.$1", + "type": "Object", + "tags": [], + "label": "soClient", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + } + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.fetchAllItems.$2", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "PackagePolicyClientFetchAllItemsOptions", + " | undefined" + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] } ], "initialIsOpen": false diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 66c958995d880..c9116c07c7ca6 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-02-06 +date: 2024-02-08 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 | |-------------------|-----------|------------------------|-----------------| -| 1221 | 3 | 1104 | 51 | +| 1229 | 3 | 1110 | 54 | ## Client diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 2e4d5fa494c10..80c2deb4cd624 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-02-06 +date: 2024-02-08 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 f9c8613429c71..a4d1a189f4d6f 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-02-06 +date: 2024-02-08 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 8343e2b8d0f44..2d95568af2c61 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-02-06 +date: 2024-02-08 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 f532c6eb76fa8..58557d1df0f81 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-02-06 +date: 2024-02-08 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 43242f9285425..605daf26ab6e8 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] --- import indexLifecycleManagementObj from './index_lifecycle_management.devdocs.json'; diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index ac163c5239946..84ce30e93ad1e 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index f8be1e039e456..4f031fef2daf0 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-02-06 +date: 2024-02-08 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 6e8564d3f56fd..1e230e88c34f0 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-02-06 +date: 2024-02-08 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 445cf4f9e9088..2df6572eec141 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index 238b68ae6c281..e8dd04b37bde9 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index ce5d3ce46f472..1f8f965aae613 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2024-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_actions_types.mdx b/api_docs/kbn_actions_types.mdx index aec13e35139c8..0763ecf7f78ca 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/actions-types'] --- import kbnActionsTypesObj from './kbn_actions_types.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index df2260a083a4f..c6126bde35517 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index 84c52499a6b8d..2362583ae493e 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2024-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerting_api_integration_helpers.mdx b/api_docs/kbn_alerting_api_integration_helpers.mdx index 20723328ec715..adae00f67b092 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-api-integration-helpers'] --- import kbnAlertingApiIntegrationHelpersObj from './kbn_alerting_api_integration_helpers.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.devdocs.json b/api_docs/kbn_alerting_state_types.devdocs.json index 344736f793222..4beda471f14be 100644 --- a/api_docs/kbn_alerting_state_types.devdocs.json +++ b/api_docs/kbn_alerting_state_types.devdocs.json @@ -51,6 +51,8 @@ "BooleanC", "; pendingRecoveredCount: ", "NumberC", + "; activeCount: ", + "NumberC", "; }>>; trackedAlertsRecovered: ", "RecordC", "<", @@ -71,6 +73,8 @@ "BooleanC", "; pendingRecoveredCount: ", "NumberC", + "; activeCount: ", + "NumberC", "; }>>; }>" ], "path": "x-pack/packages/kbn-alerting-state-types/src/lifecycle_state.ts", @@ -255,7 +259,7 @@ "label": "TrackedLifecycleAlertState", "description": [], "signature": [ - "{ alertId: string; alertUuid: string; started: string; flappingHistory: boolean[]; flapping: boolean; pendingRecoveredCount: number; }" + "{ alertId: string; alertUuid: string; started: string; flappingHistory: boolean[]; flapping: boolean; pendingRecoveredCount: number; activeCount: number; }" ], "path": "x-pack/packages/kbn-alerting-state-types/src/lifecycle_state.ts", "deprecated": false, @@ -272,7 +276,7 @@ "\nThis is redefined instead of derived from above `wrappedStateRt` because\nthere's no easy way to instantiate generic values such as the runtime type\nfactory function." ], "signature": [ - "RuleTypeState & { wrapped: State; trackedAlerts: Record; trackedAlertsRecovered: Record; }" + "RuleTypeState & { wrapped: State; trackedAlerts: Record; trackedAlertsRecovered: Record; }" ], "path": "x-pack/packages/kbn-alerting-state-types/src/lifecycle_state.ts", "deprecated": false, diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index 6703b879196ea..6cf39a09235b2 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-02-06 +date: 2024-02-08 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 4e5b7eae73ef6..dd559ffd26fe5 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-types'] --- import kbnAlertingTypesObj from './kbn_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index fc394d79a70ae..0808269e690c2 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-as-data-utils'] --- import kbnAlertsAsDataUtilsObj from './kbn_alerts_as_data_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index 536b17f36b053..4456a7af94562 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-02-06 +date: 2024-02-08 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 5daa2678e943a..2c154af34afe2 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 6c83719d87e54..3a3ddaf341bb6 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2024-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_collection_utils.mdx b/api_docs/kbn_analytics_collection_utils.mdx index 09c6d66d5590f..938d7d35faf27 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-collection-utils'] --- import kbnAnalyticsCollectionUtilsObj from './kbn_analytics_collection_utils.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index e43782aae087f..6429e4fd664f6 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2024-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] --- import kbnAnalyticsShippersElasticV3BrowserObj from './kbn_analytics_shippers_elastic_v3_browser.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index 0adde9559033e..1dd782b9c7090 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2024-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] --- import kbnAnalyticsShippersElasticV3CommonObj from './kbn_analytics_shippers_elastic_v3_common.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index fc2c28465b139..7dfc5486d4500 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2024-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] --- import kbnAnalyticsShippersElasticV3ServerObj from './kbn_analytics_shippers_elastic_v3_server.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index 944500719f35f..96aa885356288 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2024-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 0e1f236cc6205..ed1531ec2001b 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index 79e56e50a2efc..3e1e47dfb25ef 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-02-06 +date: 2024-02-08 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 dbbc48448d5bc..25d3e9cc79ec3 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index b2826caab1293..0ac7a3a59f241 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index ff198bca5c57a..f75fe45564b3b 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-02-06 +date: 2024-02-08 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 8aeccc779a416..d46de8af8627c 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-02-06 +date: 2024-02-08 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 44e2bc204c6ea..602a8996070c3 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-02-06 +date: 2024-02-08 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 a2f54c4292bab..f861e1c0050c4 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-02-06 +date: 2024-02-08 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 de3f668adfbee..1767a5304bdc0 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index 2cb17caad46dc..1ec35fd6fe8f5 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-02-06 +date: 2024-02-08 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 efc21e990d795..b771c0d3d6920 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-02-06 +date: 2024-02-08 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 05739177ae73e..cd2a38645c91e 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-02-06 +date: 2024-02-08 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 68354bffc37c0..0b55255cef877 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-02-06 +date: 2024-02-08 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 8c66098c6f5dc..58fb180401961 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-02-06 +date: 2024-02-08 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 c264a1e611057..b9335f88725a8 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-02-06 +date: 2024-02-08 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 2bf610f5bc5c8..c9c712c09b190 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_code_editor.devdocs.json b/api_docs/kbn_code_editor.devdocs.json index 876090f16122a..c523a040f5128 100644 --- a/api_docs/kbn_code_editor.devdocs.json +++ b/api_docs/kbn_code_editor.devdocs.json @@ -542,6 +542,22 @@ "path": "packages/shared-ux/code_editor/impl/code_editor.tsx", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/code-editor", + "id": "def-common.CodeEditorProps.fitToContent", + "type": "Object", + "tags": [], + "label": "fitToContent", + "description": [ + "\nEnables the editor to grow vertically to fit its content.\nThis option overrides the `height` option." + ], + "signature": [ + "{ minLines?: number | undefined; maxLines?: number | undefined; } | undefined" + ], + "path": "packages/shared-ux/code_editor/impl/code_editor.tsx", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/kbn_code_editor.mdx b/api_docs/kbn_code_editor.mdx index 27a1e1ff8ae79..320fcdf6a12d8 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.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 | |-------------------|-----------|------------------------|-----------------| -| 36 | 0 | 15 | 0 | +| 37 | 0 | 15 | 0 | ## Common diff --git a/api_docs/kbn_code_editor_mock.mdx b/api_docs/kbn_code_editor_mock.mdx index 1b5af8cbaf281..6353e16c8bcfa 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-02-06 +date: 2024-02-08 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 ebbd487dd641c..b500748561914 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-owners'] --- import kbnCodeOwnersObj from './kbn_code_owners.devdocs.json'; diff --git a/api_docs/kbn_coloring.devdocs.json b/api_docs/kbn_coloring.devdocs.json index b16ebabd965d3..64eb4e83e7721 100644 --- a/api_docs/kbn_coloring.devdocs.json +++ b/api_docs/kbn_coloring.devdocs.json @@ -435,6 +435,39 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.getActivePaletteName", + "type": "Function", + "tags": [], + "label": "getActivePaletteName", + "description": [], + "signature": [ + "(name: string | undefined) => string" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.getActivePaletteName.$1", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/coloring", "id": "def-common.getAssignmentColor", @@ -2754,6 +2787,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.COMPLEMENTARY_PALETTE", + "type": "string", + "tags": [], + "label": "COMPLEMENTARY_PALETTE", + "description": [], + "signature": [ + "\"complementary\"" + ], + "path": "packages/kbn-coloring/src/palettes/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/coloring", "id": "def-common.CUSTOM_PALETTE", @@ -2799,6 +2847,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.DEFAULT_FALLBACK_PALETTE", + "type": "string", + "tags": [], + "label": "DEFAULT_FALLBACK_PALETTE", + "description": [], + "signature": [ + "\"default\"" + ], + "path": "packages/kbn-coloring/src/palettes/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/coloring", "id": "def-common.DEFAULT_MAX_STOP", @@ -2919,6 +2982,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.LEGACY_COMPLIMENTARY_PALETTE", + "type": "string", + "tags": [], + "label": "LEGACY_COMPLIMENTARY_PALETTE", + "description": [], + "signature": [ + "\"complimentary\"" + ], + "path": "packages/kbn-coloring/src/palettes/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/coloring", "id": "def-common.NEUTRAL_COLOR_DARK", diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 60fe82bc31c94..f675651096bce 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] --- import kbnColoringObj from './kbn_coloring.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 206 | 0 | 169 | 8 | +| 211 | 0 | 174 | 8 | ## Common diff --git a/api_docs/kbn_config.devdocs.json b/api_docs/kbn_config.devdocs.json index 1e255c294bc37..99fd12bd40eb3 100644 --- a/api_docs/kbn_config.devdocs.json +++ b/api_docs/kbn_config.devdocs.json @@ -39,7 +39,7 @@ "\nInformation about Kibana package (version, build number etc.)." ], "signature": [ - "{ readonly version: string; readonly branch: string; readonly buildNum: number; readonly buildSha: string; readonly buildDate: Date; readonly buildFlavor: ", + "{ readonly version: string; readonly branch: string; readonly buildNum: number; readonly buildSha: string; readonly buildShaShort: string; readonly buildDate: Date; readonly buildFlavor: ", { "pluginId": "@kbn/config", "scope": "common", @@ -964,6 +964,17 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/config", + "id": "def-common.PackageInfo.buildShaShort", + "type": "string", + "tags": [], + "label": "buildShaShort", + "description": [], + "path": "packages/kbn-config/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/config", "id": "def-common.PackageInfo.buildDate", diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index efedb2a0e520a..352c9fbf5774d 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] --- import kbnConfigObj from './kbn_config.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 | |-------------------|-----------|------------------------|-----------------| -| 76 | 0 | 47 | 9 | +| 77 | 0 | 48 | 9 | ## Common diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index 392f659dcd19e..b8fa2ab445158 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-02-06 +date: 2024-02-08 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 c1a7b2ed26843..9bf2e8798378d 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-02-06 +date: 2024-02-08 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 95016c6839b28..288b0cd8429f2 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-editor'] --- import kbnContentManagementContentEditorObj from './kbn_content_management_content_editor.devdocs.json'; diff --git a/api_docs/kbn_content_management_tabbed_table_list_view.mdx b/api_docs/kbn_content_management_tabbed_table_list_view.mdx index da721805655fc..83f0afa466430 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-02-06 +date: 2024-02-08 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 a60b87e63ff11..29e3e3fb6e9de 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-02-06 +date: 2024-02-08 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 58feea4bcb91c..3caf4dedb360d 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-02-06 +date: 2024-02-08 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 7797000dd702f..43b3aa976cf31 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-table'] --- import kbnContentManagementTableListViewTableObj from './kbn_content_management_table_list_view_table.devdocs.json'; diff --git a/api_docs/kbn_content_management_utils.mdx b/api_docs/kbn_content_management_utils.mdx index e7ab24de48253..0f4c9036bee49 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-utils'] --- import kbnContentManagementUtilsObj from './kbn_content_management_utils.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index 65f58da3fee2b..5c4feb84eb1ba 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-02-06 +date: 2024-02-08 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 875b3c3eb5bce..07136a907a3bf 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-02-06 +date: 2024-02-08 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 d6b8238051636..63258f3c47d15 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-mocks'] --- import kbnCoreAnalyticsBrowserMocksObj from './kbn_core_analytics_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server.mdx b/api_docs/kbn_core_analytics_server.mdx index 13abfb178cd46..695d28c0134d8 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-02-06 +date: 2024-02-08 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 2496d46c72698..a2b32db67bddb 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-02-06 +date: 2024-02-08 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 a5bfbf4f9683a..244fcfaf34db5 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-02-06 +date: 2024-02-08 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 aaf8715eb5d63..39ee36437db30 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-02-06 +date: 2024-02-08 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 7f1def4d0a2ad..78467a9be966a 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-02-06 +date: 2024-02-08 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 a961d37be58cd..c943b2ba31290 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-02-06 +date: 2024-02-08 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 2c88d63ab0d86..9c6a61750d08a 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-02-06 +date: 2024-02-08 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 228c2020f3698..925f06201ac93 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-02-06 +date: 2024-02-08 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 01fa939f79155..007e706834014 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-02-06 +date: 2024-02-08 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 dd124ffcaeddb..822678a4634ca 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-02-06 +date: 2024-02-08 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 71458bff2a577..de1c6e41bde19 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-02-06 +date: 2024-02-08 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 b0a1c5876528f..487ca8516e901 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-02-06 +date: 2024-02-08 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 69697d6d2f471..8f3d657ed031f 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-02-06 +date: 2024-02-08 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 d5b965e11d2c3..13aaee49fb3d6 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-02-06 +date: 2024-02-08 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 ef2e9f2a5a745..87400852bf5ba 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-02-06 +date: 2024-02-08 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 3563e206a964b..155e3d1ab32c1 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-02-06 +date: 2024-02-08 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 f6d96fedc306c..cf9a67511dbd2 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-02-06 +date: 2024-02-08 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 e6c1c078cbceb..29018e77c93a7 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server-mocks'] --- import kbnCoreCapabilitiesServerMocksObj from './kbn_core_capabilities_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index 8e04646308234..7e248c277a53a 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser'] --- import kbnCoreChromeBrowserObj from './kbn_core_chrome_browser.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index d1d29177c928b..c11368f3af3d9 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-02-06 +date: 2024-02-08 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 d116b29ce8eec..4ae9332297a09 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-02-06 +date: 2024-02-08 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 fee46feb5b074..2facaddb22fdc 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-02-06 +date: 2024-02-08 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 b569fa83916a7..ab719f09e2c63 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-02-06 +date: 2024-02-08 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 6b7afd38ef673..b2f84279964e6 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-02-06 +date: 2024-02-08 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 f0b8e07e2e9d5..d70220d140f0e 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-02-06 +date: 2024-02-08 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 2e3173be55ed4..5d8aa78a7d4a6 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-02-06 +date: 2024-02-08 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 29bb19e33df55..6007871fbeb4f 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-02-06 +date: 2024-02-08 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 438c85d09c14c..b12a9b9373f49 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-02-06 +date: 2024-02-08 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 21b0c35b5502b..fc28e7abd3ced 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-02-06 +date: 2024-02-08 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 6fb03f0aa5eb3..d8e9e2e70cb95 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-02-06 +date: 2024-02-08 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 87f6efa4c6a66..06793bda57298 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-02-06 +date: 2024-02-08 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 fd55ac491cd74..97b6a368d6205 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-02-06 +date: 2024-02-08 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 a69316b541d86..06a9e3bc8b2aa 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-02-06 +date: 2024-02-08 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 27c608dd91302..484362bc9717c 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-02-06 +date: 2024-02-08 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 60128b006cf71..2b1496836fd14 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-02-06 +date: 2024-02-08 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 ed0e3a6bdfcdf..da4db3f2e5a39 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-02-06 +date: 2024-02-08 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 c98ff042a8a1c..4deae23e311bb 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-02-06 +date: 2024-02-08 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 a800948123544..a96a8402e95b2 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-02-06 +date: 2024-02-08 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 c4e2b026fb5ed..7dafc9670937a 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-02-06 +date: 2024-02-08 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 60131299942e3..00e21b7c8c4cd 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-02-06 +date: 2024-02-08 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 a88a158600a1c..61096379c1a35 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-02-06 +date: 2024-02-08 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 40c2774754ed6..914a607ba4510 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-02-06 +date: 2024-02-08 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 e49fdfbd81633..f9b7fc07ef57f 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-02-06 +date: 2024-02-08 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 a5ca3bde5e013..f45c0e2ba281b 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-02-06 +date: 2024-02-08 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 c72c70190e533..fe2b0d418efe9 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-02-06 +date: 2024-02-08 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 97ec876ed71c1..de245988d874d 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-02-06 +date: 2024-02-08 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 978eef3c68a06..2e24c70d937cc 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-02-06 +date: 2024-02-08 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 938a4a00244c5..3eeefff838d59 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-02-06 +date: 2024-02-08 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 55864a8544202..6aa09d853c97b 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-02-06 +date: 2024-02-08 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 1882082ad9c83..74f788fde9c4a 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-02-06 +date: 2024-02-08 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 fedd31f22a2df..4d9bad9a99e2a 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-02-06 +date: 2024-02-08 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 f361165a1596b..f3bcc14e633c8 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-02-06 +date: 2024-02-08 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 0789371fbea3d..5fc3117502e5c 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-02-06 +date: 2024-02-08 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 acc9c2857d08c..1627580848aa5 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-02-06 +date: 2024-02-08 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 f325cc00a78ce..df4b66973facb 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser-mocks'] --- import kbnCoreFatalErrorsBrowserMocksObj from './kbn_core_fatal_errors_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index b33b06c0ea599..a612f81fdcfd3 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-02-06 +date: 2024-02-08 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 538b483546455..f2e5236be83a0 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-02-06 +date: 2024-02-08 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 a9e748cede531..f6eee4cf9a021 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-02-06 +date: 2024-02-08 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 02303e8c6e9e2..c376893e3d624 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-02-06 +date: 2024-02-08 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 e4e95e1c2ded5..b362fe116e723 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-02-06 +date: 2024-02-08 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 170bf5bef5255..eeb0f260e6409 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-02-06 +date: 2024-02-08 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 ae0385605894a..d26c55f0fb0df 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-02-06 +date: 2024-02-08 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 e4ea770897c34..21c62de395267 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-02-06 +date: 2024-02-08 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 7217d645f1649..d211d7411945e 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-02-06 +date: 2024-02-08 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 bee5f215e4883..4250092d3f05a 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-02-06 +date: 2024-02-08 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 4e201f8fd0edf..08f56b9a1ac97 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-02-06 +date: 2024-02-08 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 9b8676b2df198..230ca9ab5e1f1 100644 --- a/api_docs/kbn_core_http_server.devdocs.json +++ b/api_docs/kbn_core_http_server.devdocs.json @@ -4108,6 +4108,10 @@ "plugin": "enterpriseSearch", "path": "x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts" }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts" + }, { "plugin": "enterpriseSearch", "path": "x-pack/plugins/enterprise_search/server/routes/enterprise_search/crawler/crawler_multiple_schedules.ts" @@ -9777,6 +9781,42 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-common.IStaticAssets.prependPublicUrl", + "type": "Function", + "tags": [ + "note" + ], + "label": "prependPublicUrl", + "description": [ + "\nWill return an href, either a path for or full URL with the provided path\nappended to the static assets public base path.\n\nUseful for instances were you need to render your own HTML page and link to\ncertain static assets.\n" + ], + "signature": [ + "(pathname: string) => string" + ], + "path": "packages/core/http/core-http-server/src/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-common.IStaticAssets.prependPublicUrl.$1", + "type": "string", + "tags": [], + "label": "pathname", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/http/core-http-server/src/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] } ], "initialIsOpen": false diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index 2196942caa8df..12f1edda40a9e 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 457 | 1 | 180 | 0 | +| 459 | 1 | 181 | 0 | ## Common diff --git a/api_docs/kbn_core_http_server_internal.devdocs.json b/api_docs/kbn_core_http_server_internal.devdocs.json index 7b138870b2d1e..1005e2432f164 100644 --- a/api_docs/kbn_core_http_server_internal.devdocs.json +++ b/api_docs/kbn_core_http_server_internal.devdocs.json @@ -619,16 +619,10 @@ "id": "def-common.HttpServer.Unnamed.$1", "type": "Object", "tags": [], - "label": "logger", + "label": "coreContext", "description": [], "signature": [ - { - "pluginId": "@kbn/logging", - "scope": "common", - "docId": "kibKbnLoggingPluginApi", - "section": "def-common.LoggerFactory", - "text": "LoggerFactory" - } + "CoreContext" ], "path": "packages/core/http/core-http-server-internal/src/http_server.ts", "deprecated": false, @@ -755,6 +749,247 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.StaticAssets", + "type": "Class", + "tags": [], + "label": "StaticAssets", + "description": [ + "\nConvention is for trailing slashes in pathnames are stripped." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server-internal", + "scope": "common", + "docId": "kibKbnCoreHttpServerInternalPluginApi", + "section": "def-common.StaticAssets", + "text": "StaticAssets" + }, + " implements ", + { + "pluginId": "@kbn/core-http-server-internal", + "scope": "common", + "docId": "kibKbnCoreHttpServerInternalPluginApi", + "section": "def-common.InternalStaticAssets", + "text": "InternalStaticAssets" + } + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.StaticAssets.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.StaticAssets.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "{ basePath, cdnConfig, shaDigest }", + "description": [], + "signature": [ + "StaticAssetsParams" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.StaticAssets.getHrefBase", + "type": "Function", + "tags": [], + "label": "getHrefBase", + "description": [ + "\nReturns a href (hypertext reference) intended to be used as the base for constructing\nother hrefs to static assets." + ], + "signature": [ + "() => string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.StaticAssets.getPluginAssetHref", + "type": "Function", + "tags": [], + "label": "getPluginAssetHref", + "description": [], + "signature": [ + "(pluginName: string, assetPath: string) => string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.StaticAssets.getPluginAssetHref.$1", + "type": "string", + "tags": [], + "label": "pluginName", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.StaticAssets.getPluginAssetHref.$2", + "type": "string", + "tags": [], + "label": "assetPath", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.StaticAssets.prependServerPath", + "type": "Function", + "tags": [], + "label": "prependServerPath", + "description": [], + "signature": [ + "(path: string) => string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.StaticAssets.prependServerPath.$1", + "type": "string", + "tags": [], + "label": "path", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.StaticAssets.prependPublicUrl", + "type": "Function", + "tags": [], + "label": "prependPublicUrl", + "description": [], + "signature": [ + "(pathname: string) => string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.StaticAssets.prependPublicUrl.$1", + "type": "string", + "tags": [], + "label": "pathname", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.StaticAssets.getPluginServerPath", + "type": "Function", + "tags": [], + "label": "getPluginServerPath", + "description": [], + "signature": [ + "(pluginName: string, assetPath: string) => string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.StaticAssets.getPluginServerPath.$1", + "type": "string", + "tags": [], + "label": "pluginName", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.StaticAssets.getPluginServerPath.$2", + "type": "string", + "tags": [], + "label": "assetPath", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false } ], "functions": [ @@ -882,7 +1117,206 @@ "initialIsOpen": false } ], - "interfaces": [], + "interfaces": [ + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.InternalStaticAssets", + "type": "Interface", + "tags": [], + "label": "InternalStaticAssets", + "description": [], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.InternalStaticAssets.getHrefBase", + "type": "Function", + "tags": [], + "label": "getHrefBase", + "description": [], + "signature": [ + "() => string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.InternalStaticAssets.getPluginAssetHref", + "type": "Function", + "tags": [], + "label": "getPluginAssetHref", + "description": [ + "\nIntended for use by server code rendering UI or generating links to static assets\nthat will ultimately be called from the browser and must respect settings like\nserverBasePath" + ], + "signature": [ + "(pluginName: string, assetPath: string) => string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.InternalStaticAssets.getPluginAssetHref.$1", + "type": "string", + "tags": [], + "label": "pluginName", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.InternalStaticAssets.getPluginAssetHref.$2", + "type": "string", + "tags": [], + "label": "assetPath", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.InternalStaticAssets.getPluginServerPath", + "type": "Function", + "tags": [], + "label": "getPluginServerPath", + "description": [ + "\nIntended for use by server code wanting to register static assets against Kibana\nas server paths" + ], + "signature": [ + "(pluginName: string, assetPath: string) => string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.InternalStaticAssets.getPluginServerPath.$1", + "type": "string", + "tags": [], + "label": "pluginName", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.InternalStaticAssets.getPluginServerPath.$2", + "type": "string", + "tags": [], + "label": "assetPath", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.InternalStaticAssets.prependServerPath", + "type": "Function", + "tags": [], + "label": "prependServerPath", + "description": [ + "\nSimilar to getPluginServerPath, but not plugin-scoped" + ], + "signature": [ + "(pathname: string) => string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.InternalStaticAssets.prependServerPath.$1", + "type": "string", + "tags": [], + "label": "pathname", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.InternalStaticAssets.prependPublicUrl", + "type": "Function", + "tags": [ + "note" + ], + "label": "prependPublicUrl", + "description": [ + "\nWill append the given path segment to the configured public path.\n" + ], + "signature": [ + "(pathname: string) => string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-server-internal", + "id": "def-common.InternalStaticAssets.prependPublicUrl.$1", + "type": "string", + "tags": [], + "label": "pathname", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], "enums": [], "misc": [ { diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index dd8b245f82d1d..82af6b2aefb75 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] --- import kbnCoreHttpServerInternalObj from './kbn_core_http_server_internal.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 58 | 0 | 52 | 9 | +| 84 | 0 | 72 | 9 | ## Common @@ -34,6 +34,9 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core ### Classes +### Interfaces + + ### Consts, variables and types diff --git a/api_docs/kbn_core_http_server_mocks.devdocs.json b/api_docs/kbn_core_http_server_mocks.devdocs.json index e3dceb95217ad..753cf161395b9 100644 --- a/api_docs/kbn_core_http_server_mocks.devdocs.json +++ b/api_docs/kbn_core_http_server_mocks.devdocs.json @@ -673,7 +673,13 @@ "description": [], "signature": [ "{ staticAssets: ", - "InternalStaticAssets", + { + "pluginId": "@kbn/core-http-server-internal", + "scope": "common", + "docId": "kibKbnCoreHttpServerInternalPluginApi", + "section": "def-common.InternalStaticAssets", + "text": "InternalStaticAssets" + }, "; isListening: jest.MockInstance; basePath: ", { "pluginId": "@kbn/core-http-server", diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index 49d9bb653fe6e..b2ae7d0501047 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-02-06 +date: 2024-02-08 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 fe1d6c5b8e711..fbfeee4e498df 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-02-06 +date: 2024-02-08 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 ba2ee1d33dae9..07d9ee7b67473 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-02-06 +date: 2024-02-08 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 b274a12dd05d8..18fa18602d6a1 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-02-06 +date: 2024-02-08 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 13357217e24d8..850d7e006f30f 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-02-06 +date: 2024-02-08 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 df12817ab9a45..0972b169c5a25 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-02-06 +date: 2024-02-08 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 3cd093904020a..2b50f03d992a4 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-02-06 +date: 2024-02-08 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 9004d9222d10b..42b46c147d31e 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-02-06 +date: 2024-02-08 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 2cd080e06f37c..44c1b89989262 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-02-06 +date: 2024-02-08 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 6feab333d9285..e36ba5bb25295 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-02-06 +date: 2024-02-08 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 c55751c63f6c8..08f8b2d90a84f 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-02-06 +date: 2024-02-08 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 9b5370c4b82af..f627928546900 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-02-06 +date: 2024-02-08 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 663337ece23ca..8189b61131732 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-02-06 +date: 2024-02-08 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 f5fcf617a256b..a59624cb0fd83 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-02-06 +date: 2024-02-08 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 65a943f74a814..2c3e194d00012 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-02-06 +date: 2024-02-08 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 8470b1a4d3dc1..0032ef1fd1c51 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-02-06 +date: 2024-02-08 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 ee4434b1803b3..fffc8316e29ef 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-02-06 +date: 2024-02-08 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 7e932afec8f04..a7649ca3c12c4 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-02-06 +date: 2024-02-08 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 84ea1690ee3d6..cb58b91d8279b 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-02-06 +date: 2024-02-08 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 1ae529f7456ea..a540b9c91e0fe 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-02-06 +date: 2024-02-08 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 b2398805a454a..e864cb1853d72 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-02-06 +date: 2024-02-08 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 6674e8316ca98..f1e1c04e375f0 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-02-06 +date: 2024-02-08 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 262e41de053e6..9149c2a0260ce 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-02-06 +date: 2024-02-08 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 6138921216905..28b259f64a7e0 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-02-06 +date: 2024-02-08 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 51418ee079d5c..c5f37a08e2077 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-02-06 +date: 2024-02-08 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 2f153b9bb19c7..d8c4209967acd 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-02-06 +date: 2024-02-08 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 49f8dcf2cab0e..29c98e37a41b4 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-02-06 +date: 2024-02-08 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 c46847bc8cb23..4942c22792c4b 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-02-06 +date: 2024-02-08 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 f0d2aa8a06586..183059e2b9c44 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-02-06 +date: 2024-02-08 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 c909c6a46f644..65d79f76f326b 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-02-06 +date: 2024-02-08 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 54a7ef7037c71..f14305177173d 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-02-06 +date: 2024-02-08 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 ac3bcf794e6b3..68fbe4722aa42 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-02-06 +date: 2024-02-08 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 48e1d28773ad8..de475e9439f19 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-02-06 +date: 2024-02-08 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 f8980967a8b5d..aa25bdb614f52 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-02-06 +date: 2024-02-08 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 8bb456a5b54c4..c40f4014e7dee 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-02-06 +date: 2024-02-08 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 eeb6564f1416c..dce50c7b8d4b4 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-02-06 +date: 2024-02-08 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 588c3a1f3aa8c..a4994bb77f7cf 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-02-06 +date: 2024-02-08 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 9702734df04ec..773b22d72fcd4 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-02-06 +date: 2024-02-08 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 84b3e36a3f838..2fc858e27c4e1 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-02-06 +date: 2024-02-08 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 450b0acd26b3f..02bff0d8eb5ba 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-02-06 +date: 2024-02-08 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 7ad27b4058429..37b5fc25c742d 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-02-06 +date: 2024-02-08 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 f9aead4dc80f3..8b50f4429447b 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-02-06 +date: 2024-02-08 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 fef386d757474..34f73f83f0084 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-02-06 +date: 2024-02-08 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 5eea0fb87076d..7e1df39b46b9e 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-02-06 +date: 2024-02-08 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 1fb2a54ca2d03..1cef382393e48 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-02-06 +date: 2024-02-08 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 8d04c99906e90..c2f86bc71c50a 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-browser'] --- import kbnCoreSavedObjectsApiBrowserObj from './kbn_core_saved_objects_api_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server.devdocs.json b/api_docs/kbn_core_saved_objects_api_server.devdocs.json index 606ddff1badbf..3b1b515c44655 100644 --- a/api_docs/kbn_core_saved_objects_api_server.devdocs.json +++ b/api_docs/kbn_core_saved_objects_api_server.devdocs.json @@ -2688,7 +2688,7 @@ }, { "plugin": "alerting", - "path": "x-pack/plugins/alerting/server/data/rule/types/rule_attributes.ts" + "path": "x-pack/plugins/alerting/common/rule.ts" }, { "plugin": "alerting", @@ -2708,7 +2708,7 @@ }, { "plugin": "alerting", - "path": "x-pack/plugins/alerting/server/rules_client/common/inject_references.ts" + "path": "x-pack/plugins/alerting/server/data/rule/types/rule_attributes.ts" }, { "plugin": "alerting", @@ -2716,7 +2716,7 @@ }, { "plugin": "alerting", - "path": "x-pack/plugins/alerting/server/types.ts" + "path": "x-pack/plugins/alerting/server/rules_client/common/inject_references.ts" }, { "plugin": "alerting", diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index a368849d16b6d..849a6191f315c 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-02-06 +date: 2024-02-08 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 985d89fac09a7..a2414bf2257d1 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-02-06 +date: 2024-02-08 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 0c9020243e0f0..a9556624a28db 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-02-06 +date: 2024-02-08 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 4ce9b3cfa38e0..e819f6d63ee60 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-02-06 +date: 2024-02-08 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 39839ac0a25a6..72d3e66799c0e 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-02-06 +date: 2024-02-08 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 99afe2bcc8878..dde0423a7b1f0 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-02-06 +date: 2024-02-08 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 6ed8b1ecd614a..fb21471738800 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-02-06 +date: 2024-02-08 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 b124f8debcf7e..8f585e5ef712f 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-02-06 +date: 2024-02-08 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 e9a23417bebb4..fdc82f9342200 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-02-06 +date: 2024-02-08 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 898fe16bac524..1de73758515db 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-02-06 +date: 2024-02-08 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 b1d3cf267ee92..d4a1266600e85 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-02-06 +date: 2024-02-08 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 3432f4b3b16eb..75ddbd88cf648 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-02-06 +date: 2024-02-08 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.devdocs.json b/api_docs/kbn_core_saved_objects_server.devdocs.json index 87524176d7202..9bcb155c4e2bb 100644 --- a/api_docs/kbn_core_saved_objects_server.devdocs.json +++ b/api_docs/kbn_core_saved_objects_server.devdocs.json @@ -6153,7 +6153,7 @@ }, { "plugin": "alerting", - "path": "x-pack/plugins/alerting/server/data/rule/types/rule_attributes.ts" + "path": "x-pack/plugins/alerting/common/rule.ts" }, { "plugin": "alerting", @@ -6173,7 +6173,7 @@ }, { "plugin": "alerting", - "path": "x-pack/plugins/alerting/server/rules_client/common/inject_references.ts" + "path": "x-pack/plugins/alerting/server/data/rule/types/rule_attributes.ts" }, { "plugin": "alerting", @@ -6181,7 +6181,7 @@ }, { "plugin": "alerting", - "path": "x-pack/plugins/alerting/server/types.ts" + "path": "x-pack/plugins/alerting/server/rules_client/common/inject_references.ts" }, { "plugin": "alerting", diff --git a/api_docs/kbn_core_saved_objects_server.mdx b/api_docs/kbn_core_saved_objects_server.mdx index 1c24ac52319fc..cce430fed1399 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-02-06 +date: 2024-02-08 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 fbbcddac68301..72f69ff5d1f65 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-02-06 +date: 2024-02-08 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 7dff2c04d722c..471aff347ea74 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-02-06 +date: 2024-02-08 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 b87494b4f06ce..512129b09266e 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index 7f097998ff595..5b8d798c407d0 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-02-06 +date: 2024-02-08 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 4495fda10856c..cce9d26f94913 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-02-06 +date: 2024-02-08 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 087f37221e833..7fd60d80240de 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-02-06 +date: 2024-02-08 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 9ae8e45740558..463681e928ae6 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-02-06 +date: 2024-02-08 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 3b15855ef32dc..dce6072b82290 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-02-06 +date: 2024-02-08 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 1b2701833b892..99627f59d3149 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-02-06 +date: 2024-02-08 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 31dfd2c745316..346820e660d43 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-02-06 +date: 2024-02-08 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 968d1581023b8..552509037b92f 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-02-06 +date: 2024-02-08 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 78938506b0427..8adfff27843f4 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-02-06 +date: 2024-02-08 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 d12acbe123996..3c59ed1ca2557 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-02-06 +date: 2024-02-08 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 3428d38ff24b5..a97bf61389c41 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-02-06 +date: 2024-02-08 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 2a2af27f31ef3..47bd2019d6f23 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-02-06 +date: 2024-02-08 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 ff13e715fc0d1..546aa1e3ad155 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-02-06 +date: 2024-02-08 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 6aa50869ac902..445a4607c33b7 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-02-06 +date: 2024-02-08 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 a9bb775f0a87f..bdc5bac248b26 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-02-06 +date: 2024-02-08 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 82f29c19d596c..065d2b61009fd 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-02-06 +date: 2024-02-08 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 65c54c8be1093..fa9458171836b 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-02-06 +date: 2024-02-08 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 8ffecb0c9c2af..2424aa3084e03 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-02-06 +date: 2024-02-08 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 ace3240eb7d64..5519d8549a24d 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-02-06 +date: 2024-02-08 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 eb49bee9ae66a..0b9921e8b4b8e 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-02-06 +date: 2024-02-08 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 db2c3f0f3535f..d49d7904218a5 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-02-06 +date: 2024-02-08 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 425ceb0714601..0326c774328d4 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-02-06 +date: 2024-02-08 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 1501cf4b26967..e740880bd1efa 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server.mdx b/api_docs/kbn_core_user_settings_server.mdx index b8c0f53bb2a23..b5b1ce3403713 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server'] --- import kbnCoreUserSettingsServerObj from './kbn_core_user_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_internal.mdx b/api_docs/kbn_core_user_settings_server_internal.mdx index fec6068427ac2..32551520e4e30 100644 --- a/api_docs/kbn_core_user_settings_server_internal.mdx +++ b/api_docs/kbn_core_user_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-internal title: "@kbn/core-user-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-internal plugin -date: 2024-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server-internal'] --- import kbnCoreUserSettingsServerInternalObj from './kbn_core_user_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_mocks.mdx b/api_docs/kbn_core_user_settings_server_mocks.mdx index 5da7b2b15aa0e..9c9d4ff2d66c9 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-02-06 +date: 2024-02-08 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 0cfff6a5347e6..52f35791bc0d8 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-02-06 +date: 2024-02-08 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 e9c5925811242..2b1aca5dd33a1 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-02-06 +date: 2024-02-08 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 b8b96ecc6067e..d68ec3c8684c4 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-02-06 +date: 2024-02-08 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 d24ee19bc4e73..c434cfaa99173 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-02-06 +date: 2024-02-08 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 828ab8627e78e..44523ffdea60f 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-02-06 +date: 2024-02-08 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 460845d7f4daf..f0f5bcc9a6ab8 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-02-06 +date: 2024-02-08 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 816344847b2ba..7bcd2e0327054 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-service'] --- import kbnDataServiceObj from './kbn_data_service.devdocs.json'; diff --git a/api_docs/kbn_data_stream_adapter.mdx b/api_docs/kbn_data_stream_adapter.mdx index c19090b52a503..a34fb29fe90da 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-02-06 +date: 2024-02-08 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.devdocs.json b/api_docs/kbn_data_view_utils.devdocs.json new file mode 100644 index 0000000000000..0bacfe7a0aa46 --- /dev/null +++ b/api_docs/kbn_data_view_utils.devdocs.json @@ -0,0 +1,45 @@ +{ + "id": "@kbn/data-view-utils", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/data-view-utils", + "id": "def-common.ESQL_TYPE", + "type": "string", + "tags": [], + "label": "ESQL_TYPE", + "description": [ + "\nESQL type constant" + ], + "signature": [ + "\"esql\"" + ], + "path": "packages/kbn-data-view-utils/src/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_data_view_utils.mdx b/api_docs/kbn_data_view_utils.mdx new file mode 100644 index 0000000000000..28e39104f298c --- /dev/null +++ b/api_docs/kbn_data_view_utils.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnDataViewUtilsPluginApi +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-02-08 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-view-utils'] +--- +import kbnDataViewUtilsObj from './kbn_data_view_utils.devdocs.json'; + + + +Contact [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 1 | 0 | 0 | 0 | + +## Common + +### Consts, variables and types + + diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index e787c9a27d73a..6ff71cdf16d4a 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-02-06 +date: 2024-02-08 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 41d4ec9b8b59a..00cc1d8fbbb53 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-02-06 +date: 2024-02-08 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 d77e79da1bbe6..325a4b8832556 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-devtools'] --- import kbnDeeplinksDevtoolsObj from './kbn_deeplinks_devtools.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_management.mdx b/api_docs/kbn_deeplinks_management.mdx index 2432b3da767c1..b9ac7bcaf6310 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-02-06 +date: 2024-02-08 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 2f26ee59c7582..5ff4c557a9c95 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-02-06 +date: 2024-02-08 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 d8b2cee06eac3..679ebd3a0add7 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-observability'] --- import kbnDeeplinksObservabilityObj from './kbn_deeplinks_observability.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_search.mdx b/api_docs/kbn_deeplinks_search.mdx index 6adc27bb6bb45..ad3405da94a4b 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-search'] --- import kbnDeeplinksSearchObj from './kbn_deeplinks_search.devdocs.json'; diff --git a/api_docs/kbn_default_nav_analytics.mdx b/api_docs/kbn_default_nav_analytics.mdx index 16f0e1e32b39e..4e1e2c5a71ea0 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-02-06 +date: 2024-02-08 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 5c69e07a049f2..e41a566fe8330 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-02-06 +date: 2024-02-08 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 00b33c7e57592..81289dd94896f 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-02-06 +date: 2024-02-08 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 93829acabe4c4..e6780c0a641ee 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-02-06 +date: 2024-02-08 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 7616688e5f3d0..52f072784425e 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-02-06 +date: 2024-02-08 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 876628413d817..e0ca875007e2d 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-02-06 +date: 2024-02-08 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 a9b08a7ac9264..6ee34d508a935 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-02-06 +date: 2024-02-08 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 32fb2f770d644..e2300f64cec1f 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_discover_utils.mdx b/api_docs/kbn_discover_utils.mdx index 62fcf0a671a04..a294b1b364648 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/discover-utils'] --- import kbnDiscoverUtilsObj from './kbn_discover_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index df299f1012b67..6c0133256e1a6 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-02-06 +date: 2024-02-08 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 55f1d9b07d683..5563163397c3f 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-02-06 +date: 2024-02-08 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 852703f630cdf..e561046d0a619 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-02-06 +date: 2024-02-08 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 38200d4216256..6a4d8f5d5c467 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs.mdx b/api_docs/kbn_ecs.mdx index 4cd147a3272ea..e55daa4491bd5 100644 --- a/api_docs/kbn_ecs.mdx +++ b/api_docs/kbn_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs title: "@kbn/ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs plugin -date: 2024-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs'] --- import kbnEcsObj from './kbn_ecs.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index 76ccb301aa058..0dbf96f3bb33a 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,14 +8,14 @@ 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; -Contact [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) for questions regarding this plugin. +Contact [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) for questions regarding this plugin. **Code health stats** diff --git a/api_docs/kbn_elastic_agent_utils.mdx b/api_docs/kbn_elastic_agent_utils.mdx index 97b89901634b0..1938fa33966bf 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-02-06 +date: 2024-02-08 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 22fe5bde11409..51f32807cd412 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-02-06 +date: 2024-02-08 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.mdx b/api_docs/kbn_elastic_assistant_common.mdx index cb3d84351eeb7..0042f37af440d 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant-common'] --- import kbnElasticAssistantCommonObj from './kbn_elastic_assistant_common.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index 0893d7529d22d..6d5ec7bcc31ea 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-02-06 +date: 2024-02-08 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 fc3b897160d52..0c03444a4d58d 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-02-06 +date: 2024-02-08 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 f96d10525c4af..7fed094aab4ea 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-02-06 +date: 2024-02-08 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 bfdf28c2ec20e..53a9a212ec39f 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-02-06 +date: 2024-02-08 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 91d8256af2de7..5ace284b455cf 100644 --- a/api_docs/kbn_es_types.devdocs.json +++ b/api_docs/kbn_es_types.devdocs.json @@ -199,6 +199,20 @@ "path": "packages/kbn-es-types/src/search.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/es-types", + "id": "def-common.ESQLSearchParams.dropNullColumns", + "type": "CompoundType", + "tags": [], + "label": "dropNullColumns", + "description": [], + "signature": [ + "boolean | 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 1324271d42108..43511f9acdd78 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-02-06 +date: 2024-02-08 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 | |-------------------|-----------|------------------------|-----------------| -| 31 | 0 | 31 | 0 | +| 32 | 0 | 32 | 0 | ## Common diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index 4b157a443cf56..63f4eb4749b36 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-02-06 +date: 2024-02-08 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_utils.mdx b/api_docs/kbn_esql_utils.mdx index 547f34558cb8b..9c9bdd7bb241b 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-utils'] --- import kbnEsqlUtilsObj from './kbn_esql_utils.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_common.mdx b/api_docs/kbn_event_annotation_common.mdx index 08e7de2f799ed..d7d786256db97 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-02-06 +date: 2024-02-08 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 d60e3e73c5bc3..fda3348004c8d 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-02-06 +date: 2024-02-08 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 bbc7cdcd8df7f..4363ed3e4e425 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-02-06 +date: 2024-02-08 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 f8e6e02165256..0218d7f7eaded 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-02-06 +date: 2024-02-08 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 4235cb3f73321..b3bfd9889784f 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-02-06 +date: 2024-02-08 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 b9a765a1e7a1e..57f1805355cf4 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index de924d6ddcd51..68f5fc6d85cc9 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-02-06 +date: 2024-02-08 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 a47592eeef1c7..eab2893cc20ce 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-02-06 +date: 2024-02-08 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 7506a658de0ac..d51ef4b1f2f9f 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-02-06 +date: 2024-02-08 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 a7bb62530a3a7..65a4dd253a177 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-02-06 +date: 2024-02-08 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.devdocs.json b/api_docs/kbn_generate_csv.devdocs.json index 2c04fe339b8d4..95e83e2c2293d 100644 --- a/api_docs/kbn_generate_csv.devdocs.json +++ b/api_docs/kbn_generate_csv.devdocs.json @@ -84,6 +84,21 @@ "id": "def-server.CsvESQLGenerator.Unnamed.$3", "type": "Object", "tags": [], + "label": "taskInstanceFields", + "description": [], + "signature": [ + "TaskInstanceFields" + ], + "path": "packages/kbn-generate-csv/src/generate_csv_esql.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/generate-csv", + "id": "def-server.CsvESQLGenerator.Unnamed.$4", + "type": "Object", + "tags": [], "label": "clients", "description": [], "signature": [ @@ -96,7 +111,7 @@ }, { "parentPluginId": "@kbn/generate-csv", - "id": "def-server.CsvESQLGenerator.Unnamed.$4", + "id": "def-server.CsvESQLGenerator.Unnamed.$5", "type": "Object", "tags": [], "label": "cancellationToken", @@ -117,7 +132,7 @@ }, { "parentPluginId": "@kbn/generate-csv", - "id": "def-server.CsvESQLGenerator.Unnamed.$5", + "id": "def-server.CsvESQLGenerator.Unnamed.$6", "type": "Object", "tags": [], "label": "logger", @@ -138,7 +153,7 @@ }, { "parentPluginId": "@kbn/generate-csv", - "id": "def-server.CsvESQLGenerator.Unnamed.$6", + "id": "def-server.CsvESQLGenerator.Unnamed.$7", "type": "Object", "tags": [], "label": "stream", diff --git a/api_docs/kbn_generate_csv.mdx b/api_docs/kbn_generate_csv.mdx index e1cb72049c4c6..b5ad08a5e6b67 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 25 | 0 | 25 | 1 | +| 26 | 0 | 26 | 1 | ## Server diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index eeb4d22e129e0..86cf924d9594a 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-02-06 +date: 2024-02-08 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 0b72e3d554cdc..873becaaa3c6d 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-02-06 +date: 2024-02-08 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 d04a04ed6c187..a58b35e04ece3 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-02-06 +date: 2024-02-08 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 472014d1613f9..0e97adc32653c 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-02-06 +date: 2024-02-08 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 a0651d103e0a3..065be3e64ed6f 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-02-06 +date: 2024-02-08 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 e4c6bf586d13a..ccf93e82a0582 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-02-06 +date: 2024-02-08 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 bc77044f72cfe..5a53175fa1945 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-02-06 +date: 2024-02-08 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 d7d3f798c8b1c..66fc7bb0560f3 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-02-06 +date: 2024-02-08 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 2528b53c34a34..1e39fcce2541b 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_infra_forge.mdx b/api_docs/kbn_infra_forge.mdx index 22fe238380ded..aab201a3c8563 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-02-06 +date: 2024-02-08 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 d981494b8edbd..012bb189e5ff8 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index a51c7212f56fd..0f8b980a24240 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index b215a975547a6..132d9f355ee3d 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-02-06 +date: 2024-02-08 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 2226d87567665..3cd69c0fc4342 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-02-06 +date: 2024-02-08 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 3dc1448fa0aee..3bcf9f12b6e30 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-ast'] --- import kbnJsonAstObj from './kbn_json_ast.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index 4c57c6fd07cb3..ed78d6a9512fb 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_language_documentation_popover.mdx b/api_docs/kbn_language_documentation_popover.mdx index ed5f8232c52b9..cb325cbe392e7 100644 --- a/api_docs/kbn_language_documentation_popover.mdx +++ b/api_docs/kbn_language_documentation_popover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation-popover title: "@kbn/language-documentation-popover" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation-popover plugin -date: 2024-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation-popover'] --- import kbnLanguageDocumentationPopoverObj from './kbn_language_documentation_popover.devdocs.json'; diff --git a/api_docs/kbn_lens_embeddable_utils.mdx b/api_docs/kbn_lens_embeddable_utils.mdx index 4818309ae805c..46c888808ca1a 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-02-06 +date: 2024-02-08 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 f19a58524b965..6f06e654f12ac 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-02-06 +date: 2024-02-08 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 eb44abe437844..905c9ddcffe9e 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-02-06 +date: 2024-02-08 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 f741c1fbdace6..a228ab8dcffa2 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-02-06 +date: 2024-02-08 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 80b99c251cadd..0a00ed61adf2c 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-02-06 +date: 2024-02-08 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 30b842fc1c90f..376460cea2fa2 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-02-06 +date: 2024-02-08 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 2b90e436b28c8..59c942dba2995 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-02-06 +date: 2024-02-08 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 5d245064c6a50..85ea9b96d8256 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-02-06 +date: 2024-02-08 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 9fccc82c6657a..ff5d6dea8b50f 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-02-06 +date: 2024-02-08 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 2a07753fc6572..90fb27b1e2bb9 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-02-06 +date: 2024-02-08 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 48973d1dc44b4..edb11ca03ab36 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-02-06 +date: 2024-02-08 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 06e2601224c1c..e9faba609ea93 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-02-06 +date: 2024-02-08 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 14cbadf9f7e87..a0dac7eb74904 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-field-definition'] --- import kbnManagementSettingsFieldDefinitionObj from './kbn_management_settings_field_definition.devdocs.json'; diff --git a/api_docs/kbn_management_settings_ids.mdx b/api_docs/kbn_management_settings_ids.mdx index 44309b4d7bfe6..6dd9c686a2139 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-ids'] --- import kbnManagementSettingsIdsObj from './kbn_management_settings_ids.devdocs.json'; diff --git a/api_docs/kbn_management_settings_section_registry.mdx b/api_docs/kbn_management_settings_section_registry.mdx index d044ebd5f34d7..e10a606cfd471 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-02-06 +date: 2024-02-08 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 c85740d0526ae..6b3eda5d4d11e 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-02-06 +date: 2024-02-08 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 8022f1827fe2d..0d06fa62e786f 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-02-06 +date: 2024-02-08 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 589ce76ac4670..faa06e322f0c6 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-storybook-config'] --- import kbnManagementStorybookConfigObj from './kbn_management_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index c459fc919d077..9756652261749 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-02-06 +date: 2024-02-08 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 5c2456534e638..f07b3f23721e8 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-02-06 +date: 2024-02-08 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 1ed88e5862fee..08ac74052b564 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-02-06 +date: 2024-02-08 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 7772fbf05a6c6..774067d7e36e4 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-02-06 +date: 2024-02-08 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 c2448dbeac55f..4680fe1735266 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-02-06 +date: 2024-02-08 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 75f6f227f960b..0110e170d6d8c 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-02-06 +date: 2024-02-08 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 6e62058076a47..36d705f16e8d5 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-02-06 +date: 2024-02-08 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 d8acc972e5f30..18b082154f11b 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-02-06 +date: 2024-02-08 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 40ba8011e9f26..285e171db74c1 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-02-06 +date: 2024-02-08 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 68706d43e3684..176825dee4909 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-02-06 +date: 2024-02-08 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 54836fba2567b..96654663d2dea 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-02-06 +date: 2024-02-08 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 fbb184a614d28..d510a01909ee2 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-error-utils'] --- import kbnMlErrorUtilsObj from './kbn_ml_error_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_in_memory_table.mdx b/api_docs/kbn_ml_in_memory_table.mdx index 4b39c2b823054..d8f360f77f5ed 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-02-06 +date: 2024-02-08 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 59706d8d98b2f..c84ce256821d1 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-02-06 +date: 2024-02-08 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 db64fb9b25597..5d6af66f4ba23 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-02-06 +date: 2024-02-08 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 842c5ecd55d92..7da63204ac6ad 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-02-06 +date: 2024-02-08 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 49a2d524d4194..a3bd012059125 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-02-06 +date: 2024-02-08 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 181a27d2f508c..391277b2a3e8e 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-02-06 +date: 2024-02-08 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 e51866d50d45d..c4fdf69dcbd0d 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-number-utils'] --- import kbnMlNumberUtilsObj from './kbn_ml_number_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index e54a874bb6b75..182b27b80693a 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-02-06 +date: 2024-02-08 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 08c3661724bc5..ebb6a2c6a8288 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-02-06 +date: 2024-02-08 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 96a746bb78938..817979a11aa55 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-02-06 +date: 2024-02-08 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 b7dc4f841cbf3..2ae01ab9f2ee5 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-02-06 +date: 2024-02-08 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 789bfb24bfddc..351b2223df9a1 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index 71081d69a1726..071b1e8d92816 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-02-06 +date: 2024-02-08 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 b67e9905b57f1..f981f2a24364e 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-02-06 +date: 2024-02-08 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 ddb44af1648ce..b46b1794d4dc6 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_mock_idp_utils.mdx b/api_docs/kbn_mock_idp_utils.mdx index 0276054e65e34..05675bd96a17d 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mock-idp-utils'] --- import kbnMockIdpUtilsObj from './kbn_mock_idp_utils.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 1bd748850dfb3..b5dcc8f0fedbb 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-02-06 +date: 2024-02-08 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 e4ebca8c7c3d0..c80254b71493d 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning'] --- import kbnObjectVersioningObj from './kbn_object_versioning.devdocs.json'; diff --git a/api_docs/kbn_observability_alert_details.mdx b/api_docs/kbn_observability_alert_details.mdx index fb6b83ae28c47..47543a13fa62d 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; diff --git a/api_docs/kbn_observability_alerting_test_data.mdx b/api_docs/kbn_observability_alerting_test_data.mdx index 6dbe1c8bd9a09..0d33b61044a5a 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-02-06 +date: 2024-02-08 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 706f0db8f8611..330ea9dfda1d7 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-get-padded-alert-time-range-util'] --- import kbnObservabilityGetPaddedAlertTimeRangeUtilObj from './kbn_observability_get_padded_alert_time_range_util.devdocs.json'; diff --git a/api_docs/kbn_openapi_bundler.mdx b/api_docs/kbn_openapi_bundler.mdx index 9c1f2f6c169cf..03c0bcf965fec 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-02-06 +date: 2024-02-08 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 af493391a409a..64095a40985f4 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-02-06 +date: 2024-02-08 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 ead372562c7e5..7567697ecd729 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-02-06 +date: 2024-02-08 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 4936d037ec022..49238d50aae42 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-02-06 +date: 2024-02-08 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 6c45eaf31e16c..94df5b5c4ffb5 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-02-06 +date: 2024-02-08 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 2c2b3fd19fe0f..2acfa54ff357f 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-02-06 +date: 2024-02-08 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 1c5f0f0c41703..94248a84fd64d 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-02-06 +date: 2024-02-08 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 0b3ba8b769775..8fa09593837ee 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-02-06 +date: 2024-02-08 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 f879b4e8236a3..a3f2320baff97 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-02-06 +date: 2024-02-08 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 0fc6b89f143f5..3bcf0ddf1337d 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_presentation_containers.devdocs.json b/api_docs/kbn_presentation_containers.devdocs.json index 333d1d5297a04..ddae3f2b0761a 100644 --- a/api_docs/kbn_presentation_containers.devdocs.json +++ b/api_docs/kbn_presentation_containers.devdocs.json @@ -681,12 +681,12 @@ { "parentPluginId": "@kbn/presentation-containers", "id": "def-common.PanelPackage.initialState", - "type": "Unknown", + "type": "Uncategorized", "tags": [], "label": "initialState", "description": [], "signature": [ - "unknown" + "object | undefined" ], "path": "packages/presentation/presentation_containers/interfaces/presentation_container.ts", "deprecated": false, @@ -957,7 +957,15 @@ "section": "def-common.PublishesLastSavedState", "text": "PublishesLastSavedState" }, - " & { registerPanelApi: (panelId: string, panelApi: ApiType) => void; removePanel: (panelId: string) => void; canRemovePanels?: (() => boolean) | undefined; replacePanel: (idToRemove: string, newPanel: ", + " & { addNewPanel: (panel: ", + { + "pluginId": "@kbn/presentation-containers", + "scope": "common", + "docId": "kibKbnPresentationContainersPluginApi", + "section": "def-common.PanelPackage", + "text": "PanelPackage" + }, + ", displaySuccessMessage?: boolean | undefined) => Promise; registerPanelApi: (panelId: string, panelApi: ApiType) => void; removePanel: (panelId: string) => void; canRemovePanels?: (() => boolean) | undefined; replacePanel: (idToRemove: string, newPanel: ", { "pluginId": "@kbn/presentation-containers", "scope": "common", diff --git a/api_docs/kbn_presentation_containers.mdx b/api_docs/kbn_presentation_containers.mdx index 0935b4bb74252..3bd18d6afa2c9 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-containers'] --- import kbnPresentationContainersObj from './kbn_presentation_containers.devdocs.json'; diff --git a/api_docs/kbn_presentation_library.mdx b/api_docs/kbn_presentation_library.mdx index 0d36335b26da5..6c622d763a4e7 100644 --- a/api_docs/kbn_presentation_library.mdx +++ b/api_docs/kbn_presentation_library.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-library title: "@kbn/presentation-library" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-library plugin -date: 2024-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-library'] --- import kbnPresentationLibraryObj from './kbn_presentation_library.devdocs.json'; diff --git a/api_docs/kbn_presentation_publishing.devdocs.json b/api_docs/kbn_presentation_publishing.devdocs.json index c6f31a7062c72..26234c10076d9 100644 --- a/api_docs/kbn_presentation_publishing.devdocs.json +++ b/api_docs/kbn_presentation_publishing.devdocs.json @@ -2120,7 +2120,9 @@ "type": "Unknown", "tags": [], "label": "embeddable", - "description": [], + "description": [ + "\nTODO: once all actions are entirely decoupled from the embeddable system, this key should be renamed to \"api\"\nto reflect the fact that this context could contain any api." + ], "signature": [ "unknown" ], diff --git a/api_docs/kbn_presentation_publishing.mdx b/api_docs/kbn_presentation_publishing.mdx index ad99ee3737084..7e7196d62bf2d 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-publishing'] --- import kbnPresentationPublishingObj from './kbn_presentation_publishing.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kib | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 150 | 0 | 115 | 3 | +| 150 | 0 | 114 | 3 | ## Common diff --git a/api_docs/kbn_profiling_utils.mdx b/api_docs/kbn_profiling_utils.mdx index 179a1ab961ec1..34f387cf84709 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-02-06 +date: 2024-02-08 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 c8cb939c8cf3f..3f57ed02c9933 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-02-06 +date: 2024-02-08 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 ab090b72f9f1e..3f790740f0cd1 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_common.mdx b/api_docs/kbn_react_kibana_context_common.mdx index 99753a56a4b31..486ef63511d1a 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-02-06 +date: 2024-02-08 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 466d6a4b769d4..fdf93464c5e05 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-02-06 +date: 2024-02-08 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 2072a990a74aa..57ae6ed612015 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-02-06 +date: 2024-02-08 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 09aa19ae4bc79..1d1c69c0d89d2 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-02-06 +date: 2024-02-08 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 92a6872e643b5..280bff6ef8090 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-02-06 +date: 2024-02-08 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 badc14cc8e9ea..49bf6c3a2c3ac 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-mount'] --- import kbnReactKibanaMountObj from './kbn_react_kibana_mount.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index ccc2cc63268c8..f0eaa388ddb2f 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-02-06 +date: 2024-02-08 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 5e4ebc3565ac9..bd7cccacd1727 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-02-06 +date: 2024-02-08 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 62cdfd40fcff3..e9377fcac668f 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-02-06 +date: 2024-02-08 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 991da947619dd..bd87e83a30dfa 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-02-06 +date: 2024-02-08 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 f9df3e1d6f757..762b1fecab40a 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv.mdx b/api_docs/kbn_reporting_export_types_csv.mdx index 4d9808caae0a5..91959d82f64b0 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-02-06 +date: 2024-02-08 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 1d2e0cb5f1450..c1962febea500 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-02-06 +date: 2024-02-08 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 f7b500e5a2356..376668862c72e 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-02-06 +date: 2024-02-08 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 3739963bf9dd7..67e0f71c7c96d 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-02-06 +date: 2024-02-08 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 9496b7901f6d7..9b14ad11f446f 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-02-06 +date: 2024-02-08 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 504ec31b49a35..391dde6c3a857 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-02-06 +date: 2024-02-08 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 64a51744fbcf0..4832a30ad3cd2 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-02-06 +date: 2024-02-08 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 45116d97eb98b..ff59b8aca02ee 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-02-06 +date: 2024-02-08 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 db291a86830ac..54d69eb52ce60 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-02-06 +date: 2024-02-08 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 d9173a65b476f..e73a8f6a7a9c1 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/resizable-layout'] --- import kbnResizableLayoutObj from './kbn_resizable_layout.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index c4f4203f935d9..fdb2f18b13f33 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_router_utils.mdx b/api_docs/kbn_router_utils.mdx index bee4c01d9855f..b0ef1a9daa8a8 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-02-06 +date: 2024-02-08 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 d62bd8b3f391a..6912366076fa1 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-02-06 +date: 2024-02-08 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 cb00e1cd85461..8ef629c666c19 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-02-06 +date: 2024-02-08 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 83edce7d987a2..04e814aafa245 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_search_api_panels.mdx b/api_docs/kbn_search_api_panels.mdx index 1e2765a78befd..9f1324396706f 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-02-06 +date: 2024-02-08 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.devdocs.json b/api_docs/kbn_search_connectors.devdocs.json index 8e4485adab1a9..c10a612df9100 100644 --- a/api_docs/kbn_search_connectors.devdocs.json +++ b/api_docs/kbn_search_connectors.devdocs.json @@ -1350,8 +1350,8 @@ "text": "ElasticsearchClient" }, ", connectorId: string, isNative: boolean) => Promise<", - "UpdateResponse", - ">" + "Result", + ">" ], "path": "packages/kbn-search-connectors/lib/update_native.ts", "deprecated": false, @@ -1898,7 +1898,7 @@ "text": "ElasticsearchClient" }, ", connectorId: string, indexName: string) => Promise<", - "WriteResponseBase", + "Result", ">" ], "path": "packages/kbn-search-connectors/lib/update_connector_index_name.ts", @@ -2171,7 +2171,7 @@ "text": "ElasticsearchClient" }, ", connectorId: string, serviceType: string) => Promise<", - "WriteResponseBase", + "Result", ">" ], "path": "packages/kbn-search-connectors/lib/update_connector_service_type.ts", @@ -2258,7 +2258,7 @@ "text": "ConnectorStatus" }, ") => Promise<", - "WriteResponseBase", + "Result", ">" ], "path": "packages/kbn-search-connectors/lib/update_connector_status.ts", diff --git a/api_docs/kbn_search_connectors.mdx b/api_docs/kbn_search_connectors.mdx index 4a20dc3f36435..24aedb4a3b174 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-02-06 +date: 2024-02-08 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 ba6a624965f79..40f59bcfabf5d 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-02-06 +date: 2024-02-08 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 0a04eafb87036..1990cfdeb5163 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-02-06 +date: 2024-02-08 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 a034030c20074..d1025cbf86dfd 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-response-warnings'] --- import kbnSearchResponseWarningsObj from './kbn_search_response_warnings.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_common.mdx b/api_docs/kbn_security_plugin_types_common.mdx index e48936a57e8b4..8bbb0930a8218 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-02-06 +date: 2024-02-08 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 eb0dc50628d06..118471660e70d 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-02-06 +date: 2024-02-08 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 f983957c43805..e61141b0b426d 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-server'] --- import kbnSecurityPluginTypesServerObj from './kbn_security_plugin_types_server.devdocs.json'; diff --git a/api_docs/kbn_security_solution_features.mdx b/api_docs/kbn_security_solution_features.mdx index 97d1145b772cb..54e7340cc6579 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-02-06 +date: 2024-02-08 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 383aeb868d213..6dbbe7716d967 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-02-06 +date: 2024-02-08 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 a9a5a80552a5f..57f3d6f24ee87 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-02-06 +date: 2024-02-08 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 ba0077491597e..2d83c4bb266e9 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-storybook-config'] --- import kbnSecuritySolutionStorybookConfigObj from './kbn_security_solution_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 25d574c3cf917..35b773bd297f0 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-02-06 +date: 2024-02-08 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 b464bbcfd170e..f683ceae72346 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-02-06 +date: 2024-02-08 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 c2dd690fe0d94..cc3531d6cf540 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-02-06 +date: 2024-02-08 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 9b2b2a341d492..66e2708e164f5 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-02-06 +date: 2024-02-08 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 a1d63457ed609..d6cd59f427a39 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] --- import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_grouping.mdx b/api_docs/kbn_securitysolution_grouping.mdx index 35ed6dea5380f..0af21b9b73ea4 100644 --- a/api_docs/kbn_securitysolution_grouping.mdx +++ b/api_docs/kbn_securitysolution_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-grouping title: "@kbn/securitysolution-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-grouping plugin -date: 2024-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-grouping'] --- import kbnSecuritysolutionGroupingObj from './kbn_securitysolution_grouping.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index 5d24b3dfb264e..3ee2cecf5d408 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-02-06 +date: 2024-02-08 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 69715001964b4..e991bd2333ada 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-02-06 +date: 2024-02-08 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 cb61e0d0384a7..e58c5f649e3aa 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-02-06 +date: 2024-02-08 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 ab83f59b9971a..571cdd7653e65 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-02-06 +date: 2024-02-08 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 b17f32e82c20d..1568546f020a7 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-02-06 +date: 2024-02-08 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 f1202e093f821..94d99d3f48855 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-02-06 +date: 2024-02-08 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 5b7cced8976d3..9b91fd6f84972 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-02-06 +date: 2024-02-08 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 f3b28d5c1dc51..568ae81bcf156 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-02-06 +date: 2024-02-08 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 4ce5ff65b39e4..bca66ee2fd246 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-02-06 +date: 2024-02-08 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 0c149fe3dc146..4752383b3776e 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-02-06 +date: 2024-02-08 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 e5e2cff0bd262..f9901c6a12902 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-02-06 +date: 2024-02-08 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 8d6715e6c1644..5eae52bb780a9 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-02-06 +date: 2024-02-08 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 c65173d239882..e8c3056a52793 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-02-06 +date: 2024-02-08 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 7602ff2991b01..bee9f0812f016 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_serverless_common_settings.mdx b/api_docs/kbn_serverless_common_settings.mdx index 76da512c06635..cdbfd3d68e37b 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-02-06 +date: 2024-02-08 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 7f698d6757aee..2c96dd4d9b872 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-02-06 +date: 2024-02-08 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 afd21ef474eef..75bd2722d6ab9 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-02-06 +date: 2024-02-08 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 ce09e222b9c3e..6b9fbf1de092d 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-02-06 +date: 2024-02-08 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 02b4f1c9695f1..d929e1e89989f 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-02-06 +date: 2024-02-08 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 8df47db0ab74e..48df79ee00ea0 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-02-06 +date: 2024-02-08 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 2275a324c2813..f5d6ddd4b25f2 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-02-06 +date: 2024-02-08 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 196bbce1d06bc..53a2ac8605a49 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-02-06 +date: 2024-02-08 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 0635a33afad9a..20b99674a4571 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-02-06 +date: 2024-02-08 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 cda917e732300..d2db7457735cc 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-02-06 +date: 2024-02-08 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 6442eeb2b4eb9..bbe9827527c08 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-02-06 +date: 2024-02-08 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 6f3105d66dc44..ca2d51f33d433 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-02-06 +date: 2024-02-08 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 54770eb79543a..72984a79628c9 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-02-06 +date: 2024-02-08 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 650edf07e823b..7306bfa692ba3 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-02-06 +date: 2024-02-08 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 2ffd7a4b71b9b..e058d1c8b8290 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-02-06 +date: 2024-02-08 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 6b4010ae5b344..992eb3e689b5d 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-02-06 +date: 2024-02-08 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 a9d059487be0c..4e6ad6347430c 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-02-06 +date: 2024-02-08 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 85ea2f5356204..63a08508889d2 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-02-06 +date: 2024-02-08 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 8c1616d34ff89..1795b1df6a204 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-02-06 +date: 2024-02-08 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 7c87846acafe6..29af4c9c2e6a9 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-02-06 +date: 2024-02-08 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 8d6d4260b4897..615fe97f99855 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-02-06 +date: 2024-02-08 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 6e214e67b8989..98bf551cc063e 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-02-06 +date: 2024-02-08 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 5bcd57c808345..4672e3cadc3f3 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-02-06 +date: 2024-02-08 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 f60540cc7cfc7..f224d4bbdbf58 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-02-06 +date: 2024-02-08 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 2119b3136ef7a..db23219fc66e7 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-02-06 +date: 2024-02-08 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 202ca03eaae58..8414d3ea81a5a 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-02-06 +date: 2024-02-08 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 8c0881e96c45b..08600d4bc6105 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-02-06 +date: 2024-02-08 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 3d77c61793232..72b4ed2b8bb39 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-02-06 +date: 2024-02-08 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 638ce190d1f8c..59cff789af06f 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-02-06 +date: 2024-02-08 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 cd54fc9267ac0..381ff98989c67 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-02-06 +date: 2024-02-08 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 d93fbd83448ff..7f2c7e99f60da 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-02-06 +date: 2024-02-08 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 8d0d7cc536ebc..d7ec3d8a1248c 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-02-06 +date: 2024-02-08 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 5e4e559083fd5..f28cbb449f751 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-02-06 +date: 2024-02-08 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 467d09ef6efdb..80319b95ccc12 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-02-06 +date: 2024-02-08 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 d11feaec25c71..c46e95f44d545 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-02-06 +date: 2024-02-08 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 e28ff141b4eaa..1c8091f51f630 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-02-06 +date: 2024-02-08 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 9dade6e956277..e9076403f55a5 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-02-06 +date: 2024-02-08 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 811ccc936df2a..360b21120b24b 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-02-06 +date: 2024-02-08 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 46ac16689d4f1..8a3f7a4b769c4 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-02-06 +date: 2024-02-08 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 3ef1c6243eebe..fccba7369485f 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-02-06 +date: 2024-02-08 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 00409bdebddfd..82de16bce0f07 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-02-06 +date: 2024-02-08 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 61a758c6dfd3d..2d669881993a9 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-02-06 +date: 2024-02-08 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 7c36ca5ff5cd5..460a6f08b0aa0 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-02-06 +date: 2024-02-08 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 d861b58020053..a9a0565ebd1b5 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] --- import kbnSharedUxStorybookMockObj from './kbn_shared_ux_storybook_mock.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index ec74b937ad555..137443224d0f9 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index c2d21afadd196..5df4a1b683e46 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-02-06 +date: 2024-02-08 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 dc8471e4cfd95..2d26ddb0e327a 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-02-06 +date: 2024-02-08 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 a7a2140d1755b..d53154842565a 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-predicates'] --- import kbnSortPredicatesObj from './kbn_sort_predicates.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 7a183b3fce7e1..23b583fd3f147 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-02-06 +date: 2024-02-08 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 3421c3fbe76ea..4b315de93040b 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-02-06 +date: 2024-02-08 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 0bb118f5c1cc8..81cda58b60340 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index 6e477790c4ae8..e2a14e589aad0 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-02-06 +date: 2024-02-08 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 2c6d0bbe545b5..75c1741fe9c68 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-02-06 +date: 2024-02-08 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 474ca0b9ebce9..3dbba0f9da53a 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-02-06 +date: 2024-02-08 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 b578040573a15..644acddd8a2c7 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-02-06 +date: 2024-02-08 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 c7619eaa4cf9e..e106e07806f97 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_text_based_editor.mdx b/api_docs/kbn_text_based_editor.mdx index 34ebd2aea1d54..b0fb3bb352a99 100644 --- a/api_docs/kbn_text_based_editor.mdx +++ b/api_docs/kbn_text_based_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-text-based-editor title: "@kbn/text-based-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/text-based-editor plugin -date: 2024-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/text-based-editor'] --- import kbnTextBasedEditorObj from './kbn_text_based_editor.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index 468076bb301ea..622af471c1655 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_triggers_actions_ui_types.mdx b/api_docs/kbn_triggers_actions_ui_types.mdx index a4961d8756b17..fe430c80f73d0 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-02-06 +date: 2024-02-08 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_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index a33be4bdb63a0..53416cc864850 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-02-06 +date: 2024-02-08 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 7f82c740cc53b..621a40aae4f3e 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-02-06 +date: 2024-02-08 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 e8e621db5c7d3..124335ac7e4ff 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-02-06 +date: 2024-02-08 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 b14a87e8df07d..315b924ee1a62 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-02-06 +date: 2024-02-08 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 1eb6c751fe1bb..a79dace22912d 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-02-06 +date: 2024-02-08 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 a67438bd8f598..b2ca4078f000e 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-02-06 +date: 2024-02-08 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 4ba759e7d01ff..97aca7830d5fd 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-02-06 +date: 2024-02-08 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 2b492c5feb05b..f391ea773eff5 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-02-06 +date: 2024-02-08 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 f80f216498af1..3abbb2ea45377 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unsaved-changes-badge'] --- import kbnUnsavedChangesBadgeObj from './kbn_unsaved_changes_badge.devdocs.json'; diff --git a/api_docs/kbn_use_tracked_promise.mdx b/api_docs/kbn_use_tracked_promise.mdx index ad59a5ff17b30..8ebfbe2031e54 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-02-06 +date: 2024-02-08 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 84ba87243798f..782d00d6fe62a 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-02-06 +date: 2024-02-08 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 9bc1a008d863f..97ce29df9137f 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-02-06 +date: 2024-02-08 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 cac623498d06d..90c8a0db6d324 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-02-06 +date: 2024-02-08 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 3eeb83a0cf672..be4a8165a1c63 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-02-06 +date: 2024-02-08 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 d29f7db4e9a35..eceeab039ad9e 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-02-06 +date: 2024-02-08 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 9e66c056c09f9..5e69d60e1e6e8 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-02-06 +date: 2024-02-08 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 3bf8c944b4bc8..6726f8c356c04 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-02-06 +date: 2024-02-08 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 0ddb8d234ac79..918b50bdfd850 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kbn_zod_helpers.mdx b/api_docs/kbn_zod_helpers.mdx index f91c25f1f07ba..9a13d897879e9 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-02-06 +date: 2024-02-08 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 ca6dcd32d4754..b10842d919fd0 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] --- import kibanaOverviewObj from './kibana_overview.devdocs.json'; diff --git a/api_docs/kibana_react.devdocs.json b/api_docs/kibana_react.devdocs.json index 047ff42231895..2dbf5cea1cffe 100644 --- a/api_docs/kibana_react.devdocs.json +++ b/api_docs/kibana_react.devdocs.json @@ -3314,7 +3314,7 @@ "label": "UrlTemplateEditor", "description": [], "signature": [ - "({ height, value, variables, onChange, placeholder, onEditor, Editor, }: React.PropsWithChildren<", + "({ height, fitToContent, value, variables, onChange, placeholder, onEditor, Editor, }: React.PropsWithChildren<", { "pluginId": "kibanaReact", "scope": "public", @@ -3333,7 +3333,7 @@ "id": "def-public.UrlTemplateEditor.$1", "type": "CompoundType", "tags": [], - "label": "{\n height = 105,\n value,\n variables,\n onChange,\n placeholder,\n onEditor,\n Editor = CodeEditor,\n}", + "label": "{\n height = 105,\n fitToContent,\n value,\n variables,\n onChange,\n placeholder,\n onEditor,\n Editor = CodeEditor,\n}", "description": [], "signature": [ "React.PropsWithChildren<", @@ -4532,6 +4532,20 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "kibanaReact", + "id": "def-public.UrlTemplateEditorProps.fitToContent", + "type": "Object", + "tags": [], + "label": "fitToContent", + "description": [], + "signature": [ + "{ minLines?: number | undefined; maxLines?: number | undefined; } | undefined" + ], + "path": "src/plugins/kibana_react/public/url_template_editor/url_template_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "kibanaReact", "id": "def-public.UrlTemplateEditorProps.variables", diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index 5c9340749a241..fa15a93b184f3 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact'] --- import kibanaReactObj from './kibana_react.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 | |-------------------|-----------|------------------------|-----------------| -| 152 | 0 | 120 | 3 | +| 153 | 0 | 121 | 3 | ## Client diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index 41a88487c0d92..748135aea0663 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-02-06 +date: 2024-02-08 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 cf2ebe35ced8b..70eafb707a20d 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-02-06 +date: 2024-02-08 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 72dfa569ce440..44ad4585cc74e 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-02-06 +date: 2024-02-08 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 786e473883e7d..d89fdf7c99069 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-02-06 +date: 2024-02-08 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 d3230751b8f3a..ed5b3c8507658 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-02-06 +date: 2024-02-08 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 ed3d43b9283cc..dd236acd18f20 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-02-06 +date: 2024-02-08 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 101992398f949..b7300edc6bdbb 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'links'] --- import linksObj from './links.devdocs.json'; diff --git a/api_docs/lists.devdocs.json b/api_docs/lists.devdocs.json index 1dce5bcba81d2..f73f8677d149f 100644 --- a/api_docs/lists.devdocs.json +++ b/api_docs/lists.devdocs.json @@ -2800,6 +2800,42 @@ ], "returnComment": [] }, + { + "parentPluginId": "lists", + "id": "def-server.ListClient.getImportFilename", + "type": "Function", + "tags": [], + "label": "getImportFilename", + "description": [ + "\nGets the filename of the imported file" + ], + "signature": [ + "({ stream }: ", + "GetImportFilename", + ") => Promise" + ], + "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "lists", + "id": "def-server.ListClient.getImportFilename.$1", + "type": "Object", + "tags": [], + "label": "{ stream }", + "description": [], + "signature": [ + "GetImportFilename" + ], + "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, { "parentPluginId": "lists", "id": "def-server.ListClient.importListItemsToStream", diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 709a76f67ba9c..95cd4b04df419 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-detection-engine](https://github.com/orgs/elastic/tea | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 224 | 0 | 96 | 51 | +| 226 | 0 | 97 | 52 | ## Client diff --git a/api_docs/logs_explorer.mdx b/api_docs/logs_explorer.mdx index 7912f40fe48b9..385bc61ba95ec 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsExplorer'] --- import logsExplorerObj from './logs_explorer.devdocs.json'; diff --git a/api_docs/logs_shared.devdocs.json b/api_docs/logs_shared.devdocs.json index e1e789c745eba..fd2f4b7d5cc16 100644 --- a/api_docs/logs_shared.devdocs.json +++ b/api_docs/logs_shared.devdocs.json @@ -6419,7 +6419,7 @@ }, " | undefined; fixedInterval?: string[] | undefined; timeZone?: string[] | undefined; timeSeriesDimension?: boolean | undefined; timeSeriesMetric?: ", "MappingTimeSeriesMetricType", - " | undefined; shortDotsEnable?: boolean | undefined; isMapped?: boolean | undefined; parentName?: string | undefined; }" + " | undefined; shortDotsEnable?: boolean | undefined; isMapped?: boolean | undefined; parentName?: string | undefined; defaultFormatter?: string | undefined; }" ], "path": "x-pack/plugins/logs_shared/common/log_views/resolved_log_view.ts", "deprecated": false, diff --git a/api_docs/logs_shared.mdx b/api_docs/logs_shared.mdx index 0939b1f917b59..d2f8d50cc8f01 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-02-06 +date: 2024-02-08 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 b6d07c65413f8..fba676f19aecd 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-02-06 +date: 2024-02-08 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 a7aa4c8766f1a..b08020847ad0f 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-02-06 +date: 2024-02-08 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 fba027ab99ad0..c31e0e69c8098 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-02-06 +date: 2024-02-08 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 1fcabbd72df03..625ffebb489f3 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'metricsDataAccess'] --- import metricsDataAccessObj from './metrics_data_access.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index 49d390b941593..7e9e89ae9fe6c 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-02-06 +date: 2024-02-08 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 74181d8ba9bfc..7926d1a9b9d7c 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-02-06 +date: 2024-02-08 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 490cba220192e..975e5081aa0c3 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-02-06 +date: 2024-02-08 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 e4734285acc07..7a6ccb760d179 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index 1d16d21a63aa6..f1e78a2a0d848 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index a386f38712dd1..1ac248f0f8035 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-02-06 +date: 2024-02-08 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 0576a8a2f4794..6b7425d854413 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-02-06 +date: 2024-02-08 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 6675f47fd28bf..134a6b479f43e 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-02-06 +date: 2024-02-08 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 53dff8eaf288f..00a2701daf52c 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant.mdx b/api_docs/observability_a_i_assistant.mdx index f4287021cca15..f28cde02e43cd 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistant'] --- import observabilityAIAssistantObj from './observability_a_i_assistant.devdocs.json'; diff --git a/api_docs/observability_logs_explorer.mdx b/api_docs/observability_logs_explorer.mdx index 0deea2f3d7f96..d47958c2a8078 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-02-06 +date: 2024-02-08 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 6232f5be089e5..ccd4ba146fa33 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-02-06 +date: 2024-02-08 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 6f23842fcb285..3d85c4cdca8b7 100644 --- a/api_docs/observability_shared.devdocs.json +++ b/api_docs/observability_shared.devdocs.json @@ -1297,6 +1297,53 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.TagsList", + "type": "Function", + "tags": [], + "label": "TagsList", + "description": [], + "signature": [ + "(props: ", + { + "pluginId": "observabilityShared", + "scope": "public", + "docId": "kibObservabilitySharedPluginApi", + "section": "def-public.TagsListProps", + "text": "TagsListProps" + }, + ") => JSX.Element" + ], + "path": "x-pack/plugins/observability_shared/public/components/tags_list/tags_list_lazy.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "observabilityShared", + "id": "def-public.TagsList.$1", + "type": "Object", + "tags": [], + "label": "props", + "description": [], + "signature": [ + { + "pluginId": "observabilityShared", + "scope": "public", + "docId": "kibObservabilitySharedPluginApi", + "section": "def-public.TagsListProps", + "text": "TagsListProps" + } + ], + "path": "x-pack/plugins/observability_shared/public/components/tags_list/tags_list_lazy.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "observabilityShared", "id": "def-public.TechnicalPreviewBadge", @@ -2797,6 +2844,108 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.TagsListProps", + "type": "Interface", + "tags": [], + "label": "TagsListProps", + "description": [], + "path": "x-pack/plugins/observability_shared/public/components/tags_list/tags_list.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "observabilityShared", + "id": "def-public.TagsListProps.onClick", + "type": "Function", + "tags": [], + "label": "onClick", + "description": [], + "signature": [ + "((tag: string) => void) | undefined" + ], + "path": "x-pack/plugins/observability_shared/public/components/tags_list/tags_list.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "observabilityShared", + "id": "def-public.TagsListProps.onClick.$1", + "type": "string", + "tags": [], + "label": "tag", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/plugins/observability_shared/public/components/tags_list/tags_list.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.TagsListProps.tags", + "type": "Array", + "tags": [], + "label": "tags", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/plugins/observability_shared/public/components/tags_list/tags_list.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.TagsListProps.numberOfTagsToDisplay", + "type": "number", + "tags": [], + "label": "numberOfTagsToDisplay", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "x-pack/plugins/observability_shared/public/components/tags_list/tags_list.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.TagsListProps.color", + "type": "string", + "tags": [], + "label": "color", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/observability_shared/public/components/tags_list/tags_list.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.TagsListProps.ignoreEmpty", + "type": "CompoundType", + "tags": [], + "label": "ignoreEmpty", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/observability_shared/public/components/tags_list/tags_list.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "observabilityShared", "id": "def-public.UXMetrics", diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index a41fbeb99d857..6321d889038b1 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-02-06 +date: 2024-02-08 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 | |-------------------|-----------|------------------------|-----------------| -| 321 | 1 | 316 | 19 | +| 330 | 1 | 325 | 19 | ## Client diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 4c05a952db363..aabeb22ebcdc9 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-02-06 +date: 2024-02-08 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 e6b7b9b56b44b..e993b1aa9f688 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-02-06 +date: 2024-02-08 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 e6356743276f6..721ce92b3aa70 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -15,13 +15,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Count | Plugins or Packages with a
public API | Number of teams | |--------------|----------|------------------------| -| 750 | 642 | 40 | +| 751 | 643 | 40 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 79704 | 229 | 68272 | 1731 | +| 79771 | 228 | 68327 | 1735 | ## Plugin Directory @@ -31,7 +31,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux @elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 2 | 0 | 2 | 0 | | | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 2 | 0 | 2 | 0 | | | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 2 | 0 | 2 | 0 | -| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 70 | 1 | 4 | 1 | +| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 69 | 0 | 4 | 1 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 835 | 1 | 804 | 51 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | The user interface for Elastic APM | 29 | 0 | 29 | 125 | | | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 9 | 0 | 9 | 0 | @@ -57,19 +57,19 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 268 | 0 | 249 | 1 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 108 | 0 | 105 | 12 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 54 | 0 | 51 | 0 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3235 | 31 | 2583 | 23 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3237 | 31 | 2585 | 23 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin provides the ability to create data views via a modal flyout inside Kibana apps | 35 | 0 | 25 | 5 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Reusable data view field editor across Kibana | 72 | 0 | 33 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Data view management app | 2 | 0 | 2 | 0 | -| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 940 | 0 | 273 | 4 | +| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 943 | 0 | 276 | 4 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | The Data Visualizer tools help you understand your data, by analyzing the metrics and fields in a log file or an existing Elasticsearch index. | 31 | 3 | 25 | 1 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | This plugin introduces the concept of dataset quality, where users can easily get an overview on the datasets they have. | 10 | 0 | 10 | 5 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 12 | 0 | 10 | 3 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains the Discover application and the saved search embeddable. | 151 | 0 | 104 | 22 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 37 | 0 | 35 | 2 | -| | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | APIs used to assess the quality of data in Elasticsearch indexes | 2 | 0 | 0 | 0 | +| | [@elastic/security-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 | | | [@elastic/security-generative-ai](https://github.com/orgs/elastic/teams/security-generative-ai) | Server APIs for the Elastic AI Assistant | 41 | 0 | 27 | 0 | -| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds embeddables service to Kibana | 557 | 1 | 452 | 8 | +| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds embeddables service to Kibana | 562 | 1 | 457 | 8 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Extends embeddable plugin with more functionality | 14 | 0 | 14 | 0 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides encryption and decryption utilities for saved objects containing sensitive information. | 53 | 0 | 46 | 1 | | | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | Adds dashboards for discovering and managing Enterprise Search products. | 5 | 0 | 5 | 0 | @@ -97,7 +97,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | The file upload plugin contains components and services for uploading a file, analyzing its data, and then importing the data into an Elasticsearch index. Supported file types include CSV, TSV, newline-delimited JSON and GeoJSON. | 84 | 0 | 84 | 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 | 2 | 0 | 2 | 0 | -| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1221 | 3 | 1104 | 51 | +| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1229 | 3 | 1110 | 54 | | ftrApis | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 68 | 0 | 14 | 5 | | globalSearchBar | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 0 | 0 | 0 | 0 | @@ -115,7 +115,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 127 | 2 | 100 | 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/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 6 | 0 | 6 | 0 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 152 | 0 | 120 | 3 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 153 | 0 | 121 | 3 | | kibanaUsageCollection | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 609 | 3 | 416 | 9 | | | [@elastic/kibana-cloud-security-posture](https://github.com/orgs/elastic/teams/kibana-cloud-security-posture) | - | 5 | 0 | 5 | 1 | @@ -124,7 +124,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 4 | 0 | 4 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 117 | 0 | 42 | 10 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | A dashboard panel for creating links to dashboards or external links. | 57 | 0 | 57 | 6 | -| | [@elastic/security-detection-engine](https://github.com/orgs/elastic/teams/security-detection-engine) | - | 224 | 0 | 96 | 51 | +| | [@elastic/security-detection-engine](https://github.com/orgs/elastic/teams/security-detection-engine) | - | 226 | 0 | 97 | 52 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | This plugin provides a LogsExplorer component using the Discover customization framework, offering several affordances specifically designed for log consumption. | 87 | 0 | 87 | 16 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | Exposes the shared components and APIs to access and visualize logs. | 302 | 0 | 276 | 32 | | logstash | [@elastic/logstash](https://github.com/orgs/elastic/teams/logstash) | - | 0 | 0 | 0 | 0 | @@ -144,7 +144,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 75 | 0 | 73 | 13 | | | [@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) | - | 14 | 0 | 14 | 0 | -| | [@elastic/observability-ui](https://github.com/orgs/elastic/teams/observability-ui) | - | 321 | 1 | 316 | 19 | +| | [@elastic/observability-ui](https://github.com/orgs/elastic/teams/observability-ui) | - | 330 | 1 | 325 | 19 | | | [@elastic/security-defend-workflows](https://github.com/orgs/elastic/teams/security-defend-workflows) | - | 23 | 0 | 23 | 7 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 2 | 0 | 2 | 0 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds a standardized Presentation panel which allows any forward ref component to interface with various Kibana systems. | 11 | 0 | 11 | 3 | @@ -253,11 +253,11 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 3 | 0 | 3 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 62 | 0 | 17 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 2 | 0 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 36 | 0 | 15 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 37 | 0 | 15 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 0 | | | [@elastic/appex-qa](https://github.com/orgs/elastic/teams/appex-qa) | - | 8 | 0 | 4 | 0 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 206 | 0 | 169 | 8 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 76 | 0 | 47 | 9 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 211 | 0 | 174 | 8 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 77 | 0 | 48 | 9 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 24 | 0 | 24 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 140 | 3 | 137 | 18 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 10 | 0 | 8 | 4 | @@ -335,8 +335,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 7 | 0 | 7 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 27 | 7 | 27 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 13 | 0 | 13 | 1 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 457 | 1 | 180 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 58 | 0 | 52 | 9 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 459 | 1 | 181 | 0 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 84 | 0 | 72 | 9 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 44 | 0 | 43 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 2 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 3 | 0 | 3 | 0 | @@ -433,6 +433,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 26 | 0 | 26 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 14 | 0 | 9 | 0 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 80 | 0 | 80 | 1 | +| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 1 | 0 | 0 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 44 | 0 | 43 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 6 | 0 | 6 | 0 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 5 | 0 | 5 | 0 | @@ -454,7 +455,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 39 | 0 | 26 | 5 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 19 | 0 | 11 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 35125 | 0 | 34718 | 0 | -| | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 13 | 0 | 5 | 0 | +| | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 13 | 0 | 5 | 0 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 35 | 0 | 34 | 0 | | | [@elastic/security-generative-ai](https://github.com/orgs/elastic/teams/security-generative-ai) | - | 104 | 0 | 84 | 6 | | | [@elastic/security-generative-ai](https://github.com/orgs/elastic/teams/security-generative-ai) | - | 59 | 0 | 57 | 0 | @@ -462,7 +463,7 @@ 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) | - | 7 | 0 | 3 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 263 | 1 | 202 | 15 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 31 | 0 | 31 | 0 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 32 | 0 | 32 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 1 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 14 | 0 | 13 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 39 | 0 | 39 | 0 | @@ -475,7 +476,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-qa](https://github.com/orgs/elastic/teams/appex-qa) | - | 550 | 6 | 510 | 2 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 1 | 0 | 0 | 0 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 1 | 0 | 1 | 0 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 25 | 0 | 25 | 1 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 26 | 0 | 26 | 1 | | | [@elastic/platform-onboarding](https://github.com/orgs/elastic/teams/platform-onboarding) | - | 49 | 0 | 47 | 0 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 33 | 3 | 24 | 6 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 3 | 0 | 3 | 0 | @@ -556,7 +557,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 1 | 0 | 1 | 0 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 42 | 0 | 40 | 0 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 10 | 0 | 10 | 0 | -| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 150 | 0 | 115 | 3 | +| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 150 | 0 | 114 | 3 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 156 | 0 | 45 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 13 | 0 | 7 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 22 | 0 | 9 | 0 | diff --git a/api_docs/presentation_panel.devdocs.json b/api_docs/presentation_panel.devdocs.json index 6368a6e57bc19..8c68c3a16831e 100644 --- a/api_docs/presentation_panel.devdocs.json +++ b/api_docs/presentation_panel.devdocs.json @@ -101,7 +101,15 @@ "section": "def-common.PublishesLastSavedState", "text": "PublishesLastSavedState" }, - " & { registerPanelApi: (panelId: string, panelApi: ApiType) => void; removePanel: (panelId: string) => void; canRemovePanels?: (() => boolean) | undefined; replacePanel: (idToRemove: string, newPanel: ", + " & { addNewPanel: (panel: ", + { + "pluginId": "@kbn/presentation-containers", + "scope": "common", + "docId": "kibKbnPresentationContainersPluginApi", + "section": "def-common.PanelPackage", + "text": "PanelPackage" + }, + ", displaySuccessMessage?: boolean | undefined) => Promise; registerPanelApi: (panelId: string, panelApi: ApiType) => void; removePanel: (panelId: string) => void; canRemovePanels?: (() => boolean) | undefined; replacePanel: (idToRemove: string, newPanel: ", { "pluginId": "@kbn/presentation-containers", "scope": "common", @@ -197,7 +205,15 @@ "section": "def-common.PublishesLastSavedState", "text": "PublishesLastSavedState" }, - " & { registerPanelApi: (panelId: string, panelApi: ApiType) => void; removePanel: (panelId: string) => void; canRemovePanels?: (() => boolean) | undefined; replacePanel: (idToRemove: string, newPanel: ", + " & { addNewPanel: (panel: ", + { + "pluginId": "@kbn/presentation-containers", + "scope": "common", + "docId": "kibKbnPresentationContainersPluginApi", + "section": "def-common.PanelPackage", + "text": "PanelPackage" + }, + ", displaySuccessMessage?: boolean | undefined) => Promise; registerPanelApi: (panelId: string, panelApi: ApiType) => void; removePanel: (panelId: string) => void; canRemovePanels?: (() => boolean) | undefined; replacePanel: (idToRemove: string, newPanel: ", { "pluginId": "@kbn/presentation-containers", "scope": "common", @@ -349,7 +365,15 @@ "section": "def-common.PublishesLastSavedState", "text": "PublishesLastSavedState" }, - " & { registerPanelApi: (panelId: string, panelApi: ApiType) => void; removePanel: (panelId: string) => void; canRemovePanels?: (() => boolean) | undefined; replacePanel: (idToRemove: string, newPanel: ", + " & { addNewPanel: (panel: ", + { + "pluginId": "@kbn/presentation-containers", + "scope": "common", + "docId": "kibKbnPresentationContainersPluginApi", + "section": "def-common.PanelPackage", + "text": "PanelPackage" + }, + ", displaySuccessMessage?: boolean | undefined) => Promise; registerPanelApi: (panelId: string, panelApi: ApiType) => void; removePanel: (panelId: string) => void; canRemovePanels?: (() => boolean) | undefined; replacePanel: (idToRemove: string, newPanel: ", { "pluginId": "@kbn/presentation-containers", "scope": "common", @@ -499,7 +523,15 @@ "section": "def-common.PublishesLastSavedState", "text": "PublishesLastSavedState" }, - " & { registerPanelApi: (panelId: string, panelApi: ApiType) => void; removePanel: (panelId: string) => void; canRemovePanels?: (() => boolean) | undefined; replacePanel: (idToRemove: string, newPanel: ", + " & { addNewPanel: (panel: ", + { + "pluginId": "@kbn/presentation-containers", + "scope": "common", + "docId": "kibKbnPresentationContainersPluginApi", + "section": "def-common.PanelPackage", + "text": "PanelPackage" + }, + ", displaySuccessMessage?: boolean | undefined) => Promise; registerPanelApi: (panelId: string, panelApi: ApiType) => void; removePanel: (panelId: string) => void; canRemovePanels?: (() => boolean) | undefined; replacePanel: (idToRemove: string, newPanel: ", { "pluginId": "@kbn/presentation-containers", "scope": "common", diff --git a/api_docs/presentation_panel.mdx b/api_docs/presentation_panel.mdx index adeac7299439e..e3aee7f0bfb71 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-02-06 +date: 2024-02-08 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 fbda6034b337c..e405fca86a3d1 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-02-06 +date: 2024-02-08 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 f6385f8c3c341..d4cfe28b7e8aa 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-02-06 +date: 2024-02-08 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 46e49804f7955..0e56d9598fef2 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-02-06 +date: 2024-02-08 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 e7dc5abaf599a..c55f1a935cf9e 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-02-06 +date: 2024-02-08 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 f41e5dd6aff98..d26e76e7ce488 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-02-06 +date: 2024-02-08 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 d4921a045d527..ef919e8821540 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index 354271bad5802..2e198ed3ddc25 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry'] --- import ruleRegistryObj from './rule_registry.devdocs.json'; diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index e83957355230b..91d4e323253ab 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-02-06 +date: 2024-02-08 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 7a62df5e4eecf..4126438f28ced 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-02-06 +date: 2024-02-08 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 d197e06b12f0c..e12526f9c89bc 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-02-06 +date: 2024-02-08 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 f23a23b21c6d9..1ebf4383f706f 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-02-06 +date: 2024-02-08 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 509e2387508dd..beb0e74f46dd4 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-02-06 +date: 2024-02-08 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 c761a95662c0e..e9ea1260a755d 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-02-06 +date: 2024-02-08 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 5c1b3b39af07e..413055e82c1ce 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-02-06 +date: 2024-02-08 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 2c75d771fe213..07273e1cf9127 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-02-06 +date: 2024-02-08 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 4e820e2cb8ff1..a8685fcc16fcd 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index c36c13c747ec5..3dca4d8a056eb 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-02-06 +date: 2024-02-08 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 705bd3950d0ef..a7ed38f454775 100644 --- a/api_docs/security_solution.devdocs.json +++ b/api_docs/security_solution.devdocs.json @@ -114,7 +114,7 @@ "label": "experimentalFeatures", "description": [], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly assistantStreamingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly newHostDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly alertSuppressionForIndicatorMatchRuleEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly jsonPrebuiltRulesDiffingEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly assistantStreamingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly newHostDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly alertSuppressionForIndicatorMatchRuleEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly jsonPrebuiltRulesDiffingEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; }" ], "path": "x-pack/plugins/security_solution/public/plugin.tsx", "deprecated": false, @@ -473,7 +473,7 @@ "label": "data", "description": [], "signature": [ - "({ type: \"eql\"; id: string; name: string; actions: { id: string; params: {} & { [k: string]: unknown; }; group: string; action_type_id: string; uuid?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; }[]; tags: string[]; setup: string; enabled: boolean; revision: number; version: number; references: string[]; interval: string; query: string; description: string; risk_score: number; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; from: string; to: string; language: \"eql\"; created_at: string; created_by: string; updated_at: string; updated_by: string; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; field: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; operator: \"equals\"; }[]; exceptions_list: { type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; id: string; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; total_enrichment_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; }; status_order: number; }; } | undefined; index?: string[] | undefined; data_view_id?: string | undefined; filters?: unknown[] | undefined; event_category_override?: string | undefined; tiebreaker_field?: string | undefined; timestamp_field?: string | undefined; } | { type: \"query\"; id: string; name: string; actions: { id: string; params: {} & { [k: string]: unknown; }; group: string; action_type_id: string; uuid?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; }[]; tags: string[]; setup: string; enabled: boolean; revision: number; version: number; references: string[]; interval: string; query: string; description: string; risk_score: number; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; from: string; to: string; language: \"kuery\" | \"lucene\"; created_at: string; created_by: string; updated_at: string; updated_by: string; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; field: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; operator: \"equals\"; }[]; exceptions_list: { type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; id: string; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; total_enrichment_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; }; status_order: number; }; } | undefined; index?: string[] | undefined; filters?: unknown[] | undefined; data_view_id?: string | undefined; saved_id?: string | undefined; response_actions?: ({ params: { query?: string | undefined; ecs_mapping?: Zod.objectOutputType<{}, Zod.ZodObject<{ field: Zod.ZodOptional; value: Zod.ZodOptional]>>; }, \"strip\", Zod.ZodTypeAny, { field?: string | undefined; value?: string | string[] | undefined; }, { field?: string | undefined; value?: string | string[] | undefined; }>, \"strip\"> | undefined; queries?: { id: string; query: string; ecs_mapping?: Zod.objectOutputType<{}, Zod.ZodObject<{ field: Zod.ZodOptional; value: Zod.ZodOptional]>>; }, \"strip\", Zod.ZodTypeAny, { field?: string | undefined; value?: string | string[] | undefined; }, { field?: string | undefined; value?: string | string[] | undefined; }>, \"strip\"> | undefined; version?: string | undefined; platform?: string | undefined; removed?: boolean | undefined; snapshot?: boolean | undefined; }[] | undefined; pack_id?: string | undefined; saved_query_id?: string | undefined; timeout?: number | undefined; }; action_type_id: \".osquery\"; } | { params: { command: \"isolate\"; comment?: string | undefined; }; action_type_id: \".endpoint\"; })[] | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; } | { type: \"saved_query\"; id: string; name: string; actions: { id: string; params: {} & { [k: string]: unknown; }; group: string; action_type_id: string; uuid?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; }[]; tags: string[]; setup: string; enabled: boolean; revision: number; version: number; references: string[]; interval: string; description: string; risk_score: number; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; from: string; to: string; language: \"kuery\" | \"lucene\"; created_at: string; created_by: string; updated_at: string; updated_by: string; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; field: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; operator: \"equals\"; }[]; exceptions_list: { type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; id: string; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; saved_id: string; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; total_enrichment_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; }; status_order: number; }; } | undefined; index?: string[] | undefined; query?: string | undefined; filters?: unknown[] | undefined; data_view_id?: string | undefined; response_actions?: ({ params: { query?: string | undefined; ecs_mapping?: Zod.objectOutputType<{}, Zod.ZodObject<{ field: Zod.ZodOptional; value: Zod.ZodOptional]>>; }, \"strip\", Zod.ZodTypeAny, { field?: string | undefined; value?: string | string[] | undefined; }, { field?: string | undefined; value?: string | string[] | undefined; }>, \"strip\"> | undefined; queries?: { id: string; query: string; ecs_mapping?: Zod.objectOutputType<{}, Zod.ZodObject<{ field: Zod.ZodOptional; value: Zod.ZodOptional]>>; }, \"strip\", Zod.ZodTypeAny, { field?: string | undefined; value?: string | string[] | undefined; }, { field?: string | undefined; value?: string | string[] | undefined; }>, \"strip\"> | undefined; version?: string | undefined; platform?: string | undefined; removed?: boolean | undefined; snapshot?: boolean | undefined; }[] | undefined; pack_id?: string | undefined; saved_query_id?: string | undefined; timeout?: number | undefined; }; action_type_id: \".osquery\"; } | { params: { command: \"isolate\"; comment?: string | undefined; }; action_type_id: \".endpoint\"; })[] | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; } | { type: \"threshold\"; id: string; name: string; actions: { id: string; params: {} & { [k: string]: unknown; }; group: string; action_type_id: string; uuid?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; }[]; tags: string[]; setup: string; enabled: boolean; revision: number; version: number; references: string[]; interval: string; query: string; description: string; risk_score: number; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; threshold: { value: number; field: (string | string[]) & (string | string[] | undefined); cardinality?: { value: number; field: string; }[] | undefined; }; from: string; to: string; language: \"kuery\" | \"lucene\"; created_at: string; created_by: string; updated_at: string; updated_by: string; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; field: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; operator: \"equals\"; }[]; exceptions_list: { type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; id: string; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; total_enrichment_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; }; status_order: number; }; } | undefined; index?: string[] | undefined; filters?: unknown[] | undefined; data_view_id?: string | undefined; saved_id?: string | undefined; alert_suppression?: { duration: { value: number; unit: \"m\" | \"h\" | \"s\"; }; } | undefined; } | { type: \"threat_match\"; id: string; name: string; actions: { id: string; params: {} & { [k: string]: unknown; }; group: string; action_type_id: string; uuid?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; }[]; tags: string[]; setup: string; enabled: boolean; revision: number; version: number; references: string[]; interval: string; query: string; description: string; risk_score: number; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; from: string; to: string; language: \"kuery\" | \"lucene\"; created_at: string; created_by: string; updated_at: string; updated_by: string; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; field: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; operator: \"equals\"; }[]; exceptions_list: { type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; id: string; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; threat_query: string; threat_mapping: { entries: { type: \"mapping\"; value: string; field: string; }[]; }[]; threat_index: string[]; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; total_enrichment_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; }; status_order: number; }; } | undefined; index?: string[] | undefined; filters?: unknown[] | undefined; data_view_id?: string | undefined; saved_id?: string | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; threat_filters?: unknown[] | undefined; threat_indicator_path?: string | undefined; threat_language?: \"lucene\" | \"kuery\" | undefined; concurrent_searches?: number | undefined; items_per_search?: number | undefined; } | { type: \"machine_learning\"; id: string; name: string; actions: { id: string; params: {} & { [k: string]: unknown; }; group: string; action_type_id: string; uuid?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; }[]; tags: string[]; setup: string; enabled: boolean; revision: number; version: number; references: string[]; interval: string; description: string; risk_score: number; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; from: string; to: string; created_at: string; created_by: string; updated_at: string; updated_by: string; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; field: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; operator: \"equals\"; }[]; exceptions_list: { type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; id: string; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; anomaly_threshold: number; machine_learning_job_id: (string | string[]) & (string | string[] | undefined); license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; total_enrichment_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; }; status_order: number; }; } | undefined; } | { type: \"new_terms\"; id: string; name: string; actions: { id: string; params: {} & { [k: string]: unknown; }; group: string; action_type_id: string; uuid?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; }[]; tags: string[]; setup: string; enabled: boolean; revision: number; version: number; references: string[]; interval: string; query: string; description: string; risk_score: number; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; from: string; to: string; language: \"kuery\" | \"lucene\"; created_at: string; created_by: string; updated_at: string; updated_by: string; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; field: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; operator: \"equals\"; }[]; exceptions_list: { type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; id: string; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; new_terms_fields: string[]; history_window_start: string; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; total_enrichment_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; }; status_order: number; }; } | undefined; index?: string[] | undefined; filters?: unknown[] | undefined; data_view_id?: string | undefined; } | { type: \"esql\"; id: string; name: string; actions: { id: string; params: {} & { [k: string]: unknown; }; group: string; action_type_id: string; uuid?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; }[]; tags: string[]; setup: string; enabled: boolean; revision: number; version: number; references: string[]; interval: string; query: string; description: string; risk_score: number; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; from: string; to: string; language: \"esql\"; created_at: string; created_by: string; updated_at: string; updated_by: string; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; field: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; operator: \"equals\"; }[]; exceptions_list: { type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; id: string; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; total_enrichment_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; }; status_order: number; }; } | undefined; })[]" + "({ type: \"eql\"; id: string; name: string; actions: { id: string; params: {} & { [k: string]: unknown; }; group: string; action_type_id: string; uuid?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; }[]; tags: string[]; setup: string; enabled: boolean; revision: number; version: number; references: string[]; interval: string; query: string; description: string; risk_score: number; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; from: string; to: string; language: \"eql\"; created_at: string; created_by: string; updated_at: string; updated_by: string; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; field: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; operator: \"equals\"; }[]; exceptions_list: { type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; id: string; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; total_enrichment_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; }; status_order: number; }; } | undefined; index?: string[] | undefined; data_view_id?: string | undefined; filters?: unknown[] | undefined; event_category_override?: string | undefined; tiebreaker_field?: string | undefined; timestamp_field?: string | undefined; } | { type: \"query\"; id: string; name: string; actions: { id: string; params: {} & { [k: string]: unknown; }; group: string; action_type_id: string; uuid?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; }[]; tags: string[]; setup: string; enabled: boolean; revision: number; version: number; references: string[]; interval: string; query: string; description: string; risk_score: number; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; from: string; to: string; language: \"kuery\" | \"lucene\"; created_at: string; created_by: string; updated_at: string; updated_by: string; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; field: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; operator: \"equals\"; }[]; exceptions_list: { type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; id: string; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; total_enrichment_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; }; status_order: number; }; } | undefined; index?: string[] | undefined; filters?: unknown[] | undefined; data_view_id?: string | undefined; saved_id?: string | undefined; response_actions?: ({ params: { query?: string | undefined; ecs_mapping?: Zod.objectOutputType<{}, Zod.ZodObject<{ field: Zod.ZodOptional; value: Zod.ZodOptional]>>; }, \"strip\", Zod.ZodTypeAny, { field?: string | undefined; value?: string | string[] | undefined; }, { field?: string | undefined; value?: string | string[] | undefined; }>, \"strip\"> | undefined; queries?: { id: string; query: string; ecs_mapping?: Zod.objectOutputType<{}, Zod.ZodObject<{ field: Zod.ZodOptional; value: Zod.ZodOptional]>>; }, \"strip\", Zod.ZodTypeAny, { field?: string | undefined; value?: string | string[] | undefined; }, { field?: string | undefined; value?: string | string[] | undefined; }>, \"strip\"> | undefined; version?: string | undefined; platform?: string | undefined; removed?: boolean | undefined; snapshot?: boolean | undefined; }[] | undefined; pack_id?: string | undefined; saved_query_id?: string | undefined; timeout?: number | undefined; }; action_type_id: \".osquery\"; } | { params: { command: \"isolate\"; comment?: string | undefined; } | { config: { field: string; overwrite: boolean; }; command: \"kill-process\" | \"suspend-process\"; comment?: string | undefined; }; action_type_id: \".endpoint\"; })[] | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; } | { type: \"saved_query\"; id: string; name: string; actions: { id: string; params: {} & { [k: string]: unknown; }; group: string; action_type_id: string; uuid?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; }[]; tags: string[]; setup: string; enabled: boolean; revision: number; version: number; references: string[]; interval: string; description: string; risk_score: number; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; from: string; to: string; language: \"kuery\" | \"lucene\"; created_at: string; created_by: string; updated_at: string; updated_by: string; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; field: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; operator: \"equals\"; }[]; exceptions_list: { type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; id: string; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; saved_id: string; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; total_enrichment_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; }; status_order: number; }; } | undefined; index?: string[] | undefined; query?: string | undefined; filters?: unknown[] | undefined; data_view_id?: string | undefined; response_actions?: ({ params: { query?: string | undefined; ecs_mapping?: Zod.objectOutputType<{}, Zod.ZodObject<{ field: Zod.ZodOptional; value: Zod.ZodOptional]>>; }, \"strip\", Zod.ZodTypeAny, { field?: string | undefined; value?: string | string[] | undefined; }, { field?: string | undefined; value?: string | string[] | undefined; }>, \"strip\"> | undefined; queries?: { id: string; query: string; ecs_mapping?: Zod.objectOutputType<{}, Zod.ZodObject<{ field: Zod.ZodOptional; value: Zod.ZodOptional]>>; }, \"strip\", Zod.ZodTypeAny, { field?: string | undefined; value?: string | string[] | undefined; }, { field?: string | undefined; value?: string | string[] | undefined; }>, \"strip\"> | undefined; version?: string | undefined; platform?: string | undefined; removed?: boolean | undefined; snapshot?: boolean | undefined; }[] | undefined; pack_id?: string | undefined; saved_query_id?: string | undefined; timeout?: number | undefined; }; action_type_id: \".osquery\"; } | { params: { command: \"isolate\"; comment?: string | undefined; } | { config: { field: string; overwrite: boolean; }; command: \"kill-process\" | \"suspend-process\"; comment?: string | undefined; }; action_type_id: \".endpoint\"; })[] | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; } | { type: \"threshold\"; id: string; name: string; actions: { id: string; params: {} & { [k: string]: unknown; }; group: string; action_type_id: string; uuid?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; }[]; tags: string[]; setup: string; enabled: boolean; revision: number; version: number; references: string[]; interval: string; query: string; description: string; risk_score: number; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; threshold: { value: number; field: (string | string[]) & (string | string[] | undefined); cardinality?: { value: number; field: string; }[] | undefined; }; from: string; to: string; language: \"kuery\" | \"lucene\"; created_at: string; created_by: string; updated_at: string; updated_by: string; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; field: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; operator: \"equals\"; }[]; exceptions_list: { type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; id: string; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; total_enrichment_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; }; status_order: number; }; } | undefined; index?: string[] | undefined; filters?: unknown[] | undefined; data_view_id?: string | undefined; saved_id?: string | undefined; alert_suppression?: { duration: { value: number; unit: \"m\" | \"h\" | \"s\"; }; } | undefined; } | { type: \"threat_match\"; id: string; name: string; actions: { id: string; params: {} & { [k: string]: unknown; }; group: string; action_type_id: string; uuid?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; }[]; tags: string[]; setup: string; enabled: boolean; revision: number; version: number; references: string[]; interval: string; query: string; description: string; risk_score: number; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; from: string; to: string; language: \"kuery\" | \"lucene\"; created_at: string; created_by: string; updated_at: string; updated_by: string; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; field: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; operator: \"equals\"; }[]; exceptions_list: { type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; id: string; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; threat_query: string; threat_mapping: { entries: { type: \"mapping\"; value: string; field: string; }[]; }[]; threat_index: string[]; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; total_enrichment_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; }; status_order: number; }; } | undefined; index?: string[] | undefined; filters?: unknown[] | undefined; data_view_id?: string | undefined; saved_id?: string | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; threat_filters?: unknown[] | undefined; threat_indicator_path?: string | undefined; threat_language?: \"lucene\" | \"kuery\" | undefined; concurrent_searches?: number | undefined; items_per_search?: number | undefined; } | { type: \"machine_learning\"; id: string; name: string; actions: { id: string; params: {} & { [k: string]: unknown; }; group: string; action_type_id: string; uuid?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; }[]; tags: string[]; setup: string; enabled: boolean; revision: number; version: number; references: string[]; interval: string; description: string; risk_score: number; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; from: string; to: string; created_at: string; created_by: string; updated_at: string; updated_by: string; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; field: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; operator: \"equals\"; }[]; exceptions_list: { type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; id: string; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; anomaly_threshold: number; machine_learning_job_id: (string | string[]) & (string | string[] | undefined); license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; total_enrichment_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; }; status_order: number; }; } | undefined; } | { type: \"new_terms\"; id: string; name: string; actions: { id: string; params: {} & { [k: string]: unknown; }; group: string; action_type_id: string; uuid?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; }[]; tags: string[]; setup: string; enabled: boolean; revision: number; version: number; references: string[]; interval: string; query: string; description: string; risk_score: number; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; from: string; to: string; language: \"kuery\" | \"lucene\"; created_at: string; created_by: string; updated_at: string; updated_by: string; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; field: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; operator: \"equals\"; }[]; exceptions_list: { type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; id: string; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; new_terms_fields: string[]; history_window_start: string; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; total_enrichment_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; }; status_order: number; }; } | undefined; index?: string[] | undefined; filters?: unknown[] | undefined; data_view_id?: string | undefined; } | { type: \"esql\"; id: string; name: string; actions: { id: string; params: {} & { [k: string]: unknown; }; group: string; action_type_id: string; uuid?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; }[]; tags: string[]; setup: string; enabled: boolean; revision: number; version: number; references: string[]; interval: string; query: string; description: string; risk_score: number; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; from: string; to: string; language: \"esql\"; created_at: string; created_by: string; updated_at: string; updated_by: string; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; field: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; operator: \"equals\"; }[]; exceptions_list: { type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; id: string; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; total_enrichment_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; }; status_order: number; }; } | undefined; })[]" ], "path": "x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/types.ts", "deprecated": false, @@ -568,7 +568,7 @@ "\nExperimental flag needed to enable the link" ], "signature": [ - "\"assistantModelEvaluation\" | \"assistantStreamingEnabled\" | \"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"chartEmbeddablesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"responseActionsSentinelOneV1Enabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"expandableFlyoutInCreateRuleEnabled\" | \"alertsPageFiltersEnabled\" | \"newUserDetailsFlyout\" | \"newUserDetailsFlyoutManagedUser\" | \"newHostDetailsFlyout\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"disableTimelineSaveTour\" | \"riskEnginePrivilegesRouteEnabled\" | \"alertSuppressionForIndicatorMatchRuleEnabled\" | \"entityAnalyticsAssetCriticalityEnabled\" | \"sentinelOneDataInAnalyzerEnabled\" | \"sentinelOneManualHostActionsEnabled\" | \"jsonPrebuiltRulesDiffingEnabled\" | \"timelineEsqlTabDisabled\" | undefined" + "\"assistantModelEvaluation\" | \"assistantStreamingEnabled\" | \"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"chartEmbeddablesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"automatedProcessActionsEnabled\" | \"responseActionsSentinelOneV1Enabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"expandableFlyoutInCreateRuleEnabled\" | \"alertsPageFiltersEnabled\" | \"newUserDetailsFlyout\" | \"newUserDetailsFlyoutManagedUser\" | \"newHostDetailsFlyout\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"disableTimelineSaveTour\" | \"riskEnginePrivilegesRouteEnabled\" | \"alertSuppressionForIndicatorMatchRuleEnabled\" | \"entityAnalyticsAssetCriticalityEnabled\" | \"sentinelOneDataInAnalyzerEnabled\" | \"sentinelOneManualHostActionsEnabled\" | \"jsonPrebuiltRulesDiffingEnabled\" | \"timelineEsqlTabDisabled\" | undefined" ], "path": "x-pack/plugins/security_solution/public/common/links/types.ts", "deprecated": false, @@ -648,7 +648,7 @@ "\nExperimental flag needed to disable the link. Opposite of experimentalKey" ], "signature": [ - "\"assistantModelEvaluation\" | \"assistantStreamingEnabled\" | \"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"chartEmbeddablesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"responseActionsSentinelOneV1Enabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"expandableFlyoutInCreateRuleEnabled\" | \"alertsPageFiltersEnabled\" | \"newUserDetailsFlyout\" | \"newUserDetailsFlyoutManagedUser\" | \"newHostDetailsFlyout\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"disableTimelineSaveTour\" | \"riskEnginePrivilegesRouteEnabled\" | \"alertSuppressionForIndicatorMatchRuleEnabled\" | \"entityAnalyticsAssetCriticalityEnabled\" | \"sentinelOneDataInAnalyzerEnabled\" | \"sentinelOneManualHostActionsEnabled\" | \"jsonPrebuiltRulesDiffingEnabled\" | \"timelineEsqlTabDisabled\" | undefined" + "\"assistantModelEvaluation\" | \"assistantStreamingEnabled\" | \"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"chartEmbeddablesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"automatedProcessActionsEnabled\" | \"responseActionsSentinelOneV1Enabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"expandableFlyoutInCreateRuleEnabled\" | \"alertsPageFiltersEnabled\" | \"newUserDetailsFlyout\" | \"newUserDetailsFlyoutManagedUser\" | \"newHostDetailsFlyout\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"disableTimelineSaveTour\" | \"riskEnginePrivilegesRouteEnabled\" | \"alertSuppressionForIndicatorMatchRuleEnabled\" | \"entityAnalyticsAssetCriticalityEnabled\" | \"sentinelOneDataInAnalyzerEnabled\" | \"sentinelOneManualHostActionsEnabled\" | \"jsonPrebuiltRulesDiffingEnabled\" | \"timelineEsqlTabDisabled\" | undefined" ], "path": "x-pack/plugins/security_solution/public/common/links/types.ts", "deprecated": false, @@ -1986,7 +1986,7 @@ "label": "experimentalFeatures", "description": [], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly assistantStreamingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly newHostDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly alertSuppressionForIndicatorMatchRuleEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly jsonPrebuiltRulesDiffingEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly assistantStreamingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly newHostDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly alertSuppressionForIndicatorMatchRuleEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly jsonPrebuiltRulesDiffingEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; }" ], "path": "x-pack/plugins/security_solution/public/types.ts", "deprecated": false, @@ -3030,7 +3030,7 @@ "label": "ConfigType", "description": [], "signature": [ - "Omit; }>, \"offeringSettings\"> & { experimentalFeatures: ", + "Omit; }>, \"offeringSettings\"> & { experimentalFeatures: ", { "pluginId": "securitySolution", "scope": "common", @@ -3105,7 +3105,7 @@ "\nThe security solution generic experimental features" ], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly assistantStreamingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly newHostDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly alertSuppressionForIndicatorMatchRuleEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly jsonPrebuiltRulesDiffingEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly assistantStreamingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly newHostDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly alertSuppressionForIndicatorMatchRuleEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly jsonPrebuiltRulesDiffingEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; }" ], "path": "x-pack/plugins/security_solution/server/plugin_contract.ts", "deprecated": false, @@ -3281,7 +3281,7 @@ "label": "ExperimentalFeatures", "description": [], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly assistantStreamingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly newHostDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly alertSuppressionForIndicatorMatchRuleEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly jsonPrebuiltRulesDiffingEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly assistantStreamingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly newHostDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly alertSuppressionForIndicatorMatchRuleEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly jsonPrebuiltRulesDiffingEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; }" ], "path": "x-pack/plugins/security_solution/common/experimental_features.ts", "deprecated": false, @@ -3330,7 +3330,7 @@ "\nA list of allowed values that can be used in `xpack.securitySolution.enableExperimental`.\nThis object is then used to validate and parse the value entered." ], "signature": [ - "{ readonly tGridEnabled: true; readonly tGridEventRenderedViewEnabled: true; readonly excludePoliciesInFilterEnabled: false; readonly kubernetesEnabled: true; readonly chartEmbeddablesEnabled: true; readonly donutChartEmbeddablesEnabled: false; readonly previewTelemetryUrlEnabled: false; readonly insightsRelatedAlertsByProcessAncestry: true; readonly extendedRuleExecutionLoggingEnabled: false; readonly assistantStreamingEnabled: false; readonly socTrendsEnabled: false; readonly responseActionsEnabled: true; readonly endpointResponseActionsEnabled: true; readonly responseActionUploadEnabled: true; readonly responseActionsSentinelOneV1Enabled: false; readonly alertsPageChartsEnabled: true; readonly alertTypeEnabled: false; readonly expandableFlyoutInCreateRuleEnabled: true; readonly alertsPageFiltersEnabled: true; readonly assistantModelEvaluation: false; readonly newUserDetailsFlyout: false; readonly newUserDetailsFlyoutManagedUser: false; readonly newHostDetailsFlyout: false; readonly riskScoringPersistence: true; readonly riskScoringRoutesEnabled: true; readonly esqlRulesDisabled: false; readonly protectionUpdatesEnabled: true; readonly disableTimelineSaveTour: false; readonly riskEnginePrivilegesRouteEnabled: true; readonly alertSuppressionForIndicatorMatchRuleEnabled: false; readonly entityAnalyticsAssetCriticalityEnabled: false; readonly sentinelOneDataInAnalyzerEnabled: false; readonly sentinelOneManualHostActionsEnabled: true; readonly jsonPrebuiltRulesDiffingEnabled: true; readonly timelineEsqlTabDisabled: false; }" + "{ readonly tGridEnabled: true; readonly tGridEventRenderedViewEnabled: true; readonly excludePoliciesInFilterEnabled: false; readonly kubernetesEnabled: true; readonly chartEmbeddablesEnabled: true; readonly donutChartEmbeddablesEnabled: false; readonly previewTelemetryUrlEnabled: false; readonly insightsRelatedAlertsByProcessAncestry: true; readonly extendedRuleExecutionLoggingEnabled: false; readonly assistantStreamingEnabled: false; readonly socTrendsEnabled: false; readonly responseActionsEnabled: true; readonly endpointResponseActionsEnabled: true; readonly responseActionUploadEnabled: true; readonly automatedProcessActionsEnabled: false; readonly responseActionsSentinelOneV1Enabled: false; readonly alertsPageChartsEnabled: true; readonly alertTypeEnabled: false; readonly expandableFlyoutInCreateRuleEnabled: true; readonly alertsPageFiltersEnabled: true; readonly assistantModelEvaluation: false; readonly newUserDetailsFlyout: false; readonly newUserDetailsFlyoutManagedUser: false; readonly newHostDetailsFlyout: false; readonly riskScoringPersistence: true; readonly riskScoringRoutesEnabled: true; readonly esqlRulesDisabled: false; readonly protectionUpdatesEnabled: true; readonly disableTimelineSaveTour: false; readonly riskEnginePrivilegesRouteEnabled: true; readonly alertSuppressionForIndicatorMatchRuleEnabled: false; readonly entityAnalyticsAssetCriticalityEnabled: false; readonly sentinelOneDataInAnalyzerEnabled: false; readonly sentinelOneManualHostActionsEnabled: true; readonly jsonPrebuiltRulesDiffingEnabled: true; readonly timelineEsqlTabDisabled: 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 ab806e2570c19..851069752f8ad 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-02-06 +date: 2024-02-08 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 83fb684b85baa..7d6805ad32c9b 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-02-06 +date: 2024-02-08 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 9e996af7b7f3a..6fb77f7079ab5 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionServerless'] --- import securitySolutionServerlessObj from './security_solution_serverless.devdocs.json'; diff --git a/api_docs/serverless.mdx b/api_docs/serverless.mdx index afc76d64388e6..09bf0ab95e78c 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-02-06 +date: 2024-02-08 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 e1aec49688897..80845375fa25e 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-02-06 +date: 2024-02-08 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 dfd9fd0161416..5f5879eeabe24 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-02-06 +date: 2024-02-08 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 28baa23abbf20..56946b555e2f2 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView'] --- import sessionViewObj from './session_view.devdocs.json'; diff --git a/api_docs/share.mdx b/api_docs/share.mdx index f93d6be301818..a8786c8438ef2 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 2615877a40f6f..3fce052eddefb 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore'] --- import snapshotRestoreObj from './snapshot_restore.devdocs.json'; diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index 4b5669be3d207..89ec19c9d8155 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-02-06 +date: 2024-02-08 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 50ed8ddde1978..145306cf77dec 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-02-06 +date: 2024-02-08 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 b4a962ff1e875..cef40b7b09a6a 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-02-06 +date: 2024-02-08 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 ae70dddd6abda..a151419fe32f7 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-02-06 +date: 2024-02-08 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 ceca201c9a3f8..b4fcc79079d2c 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-02-06 +date: 2024-02-08 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 cf45c9305631f..e1b5a553f14c0 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-02-06 +date: 2024-02-08 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 85c5f17ce7cf1..d65290e05ca4e 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-02-06 +date: 2024-02-08 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 2dce5b8964218..09f984ea18369 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/text_based_languages.mdx b/api_docs/text_based_languages.mdx index f583dd7b65e43..e6dc10f5a9565 100644 --- a/api_docs/text_based_languages.mdx +++ b/api_docs/text_based_languages.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/textBasedLanguages title: "textBasedLanguages" image: https://source.unsplash.com/400x175/?github description: API docs for the textBasedLanguages plugin -date: 2024-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'textBasedLanguages'] --- import textBasedLanguagesObj from './text_based_languages.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index 1c6ec938eb49a..72fe54da292e5 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-02-06 +date: 2024-02-08 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 f83e497d28b6a..51379efcc39aa 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-02-06 +date: 2024-02-08 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 24491c857335a..a5efbd6742623 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.devdocs.json b/api_docs/triggers_actions_ui.devdocs.json index 43889de19ef3d..4a9016b138d68 100644 --- a/api_docs/triggers_actions_ui.devdocs.json +++ b/api_docs/triggers_actions_ui.devdocs.json @@ -5102,7 +5102,7 @@ "label": "setRuleProperty", "description": [], "signature": [ - "(key: Prop, value: ", + "(key: Prop, value: ", "SanitizedRule", "[Prop] | null) => void" ], diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index b6db50861a066..7f22a66a59803 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-02-06 +date: 2024-02-08 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 dcd3834f37d19..1ef2d79fb3bc5 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index 4ed23567a6e68..4c659d410fcbe 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-02-06 +date: 2024-02-08 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 01ef74424e1b2..7e89032b71d6b 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-02-06 +date: 2024-02-08 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 aa0bbd2d32e78..9690c84929005 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-02-06 +date: 2024-02-08 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 7b5fcf9e147f7..1465ebdda2291 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-02-06 +date: 2024-02-08 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 d63253fa6e08b..90172fc462ad1 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-02-06 +date: 2024-02-08 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 ca74eeb826c04..79e7ca4b21364 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-02-06 +date: 2024-02-08 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 1340273a664fc..9e90600856309 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-02-06 +date: 2024-02-08 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 336a32edb6db9..0d44be7793e6a 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-02-06 +date: 2024-02-08 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 0ae5fe27283f4..36f67fe934a29 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-02-06 +date: 2024-02-08 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 2e331e3c556a3..2b55d86ea4cb4 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-02-06 +date: 2024-02-08 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 9c3a5ad02d03f..2c07f3ec3582e 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-02-06 +date: 2024-02-08 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 4ddda8b91e1b6..2feb7aa00cc8e 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-02-06 +date: 2024-02-08 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 014f02b6b1f39..eb97b5b933dbc 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-02-06 +date: 2024-02-08 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 8c0d6dad016b3..600d5be2346b4 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-02-06 +date: 2024-02-08 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 cb7d00efda18b..7fe24b693cb89 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-02-06 +date: 2024-02-08 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 e1388019e8f5f..7ed7ba8f3f4fc 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-02-06 +date: 2024-02-08 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 827e071dad140..ca95e9f9fa272 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-02-06 +date: 2024-02-08 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 2163580af6b44..bbad04fca0ceb 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-02-06 +date: 2024-02-08 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 c9255a3e35a4a..39c2e0ca0b9a7 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.devdocs.json b/api_docs/visualizations.devdocs.json index 3978b9dbad73f..a6e2e2f68542e 100644 --- a/api_docs/visualizations.devdocs.json +++ b/api_docs/visualizations.devdocs.json @@ -6848,7 +6848,15 @@ "section": "def-common.PublishesLastSavedState", "text": "PublishesLastSavedState" }, - " & { registerPanelApi: (panelId: string, panelApi: ApiType) => void; removePanel: (panelId: string) => void; canRemovePanels?: (() => boolean) | undefined; replacePanel: (idToRemove: string, newPanel: ", + " & { addNewPanel: (panel: ", + { + "pluginId": "@kbn/presentation-containers", + "scope": "common", + "docId": "kibKbnPresentationContainersPluginApi", + "section": "def-common.PanelPackage", + "text": "PanelPackage" + }, + ", displaySuccessMessage?: boolean | undefined) => Promise; registerPanelApi: (panelId: string, panelApi: ApiType) => void; removePanel: (panelId: string) => void; canRemovePanels?: (() => boolean) | undefined; replacePanel: (idToRemove: string, newPanel: ", { "pluginId": "@kbn/presentation-containers", "scope": "common", diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index fd4b0f082a8a9..186762e92bbc1 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-02-06 +date: 2024-02-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/config/serverless.oblt.yml b/config/serverless.oblt.yml index e30424b5355a4..e74210acbd2d9 100644 --- a/config/serverless.oblt.yml +++ b/config/serverless.oblt.yml @@ -39,7 +39,11 @@ xpack.uptime.service.tls.certificate: /mnt/elastic-internal/http-certs/tls.crt xpack.uptime.service.tls.key: /mnt/elastic-internal/http-certs/tls.key # Fleet specific configuration -xpack.fleet.internal.registry.capabilities: ['apm', 'observability'] +xpack.fleet.internal.registry.capabilities: [ + 'apm', + 'observability', + 'uptime', +] xpack.fleet.internal.registry.kibanaVersionCheckEnabled: false xpack.fleet.internal.registry.spec.max: '3.0' # Temporary until all packages implement new spec https://github.com/elastic/kibana/issues/166742 diff --git a/dev_docs/tutorials/ci.mdx b/dev_docs/tutorials/ci.mdx index 2234143c32664..2c093169e8b3d 100644 --- a/dev_docs/tutorials/ci.mdx +++ b/dev_docs/tutorials/ci.mdx @@ -51,6 +51,10 @@ Build an archive that can be used to serve Kibana's static assets. Build cloud Docker images that can be used for testing deployments on Elastic Cloud. +#### `ci:build-docker-fips` + +Build Docker UBI x64 image with FIPS enabled. + #### `ci:build-os-packages` Build Docker images, and Debian and RPM packages. diff --git a/docs/settings/reporting-settings.asciidoc b/docs/settings/reporting-settings.asciidoc index 192a47e184c43..ab80c11139a50 100644 --- a/docs/settings/reporting-settings.asciidoc +++ b/docs/settings/reporting-settings.asciidoc @@ -232,10 +232,10 @@ You may need to lower this setting if the default number of documents creates a ============ `xpack.reporting.csv.scroll.duration`:: -Amount of {time-units}[time] allowed before {kib} cleans the scroll context during a CSV export. Defaults to `30s`. +Amount of {time-units}[time] allowed before {kib} cleans the scroll context during a CSV export. Valid option is either `auto` or {time-units}[time], Defaults to `30s`. [NOTE] ============ -If search latency in {es} is sufficiently high, such as if you are using {ccs}, you may need to increase the setting. +If search latency in {es} is sufficiently high, such as if you are using {ccs}, you may either need to increase the time setting or set this config value to `auto`. When the config value is set to `auto` the scroll context will be preserved for as long as is possible, before the report task is terminated due to the limits of `xpack.reporting.queue.timeout`. ============ `xpack.reporting.csv.scroll.strategy`:: diff --git a/examples/embeddable_examples/public/migrations/migrations_embeddable_factory.ts b/examples/embeddable_examples/public/migrations/migrations_embeddable_factory.ts index fb96fbff77b35..3dea64a742161 100644 --- a/examples/embeddable_examples/public/migrations/migrations_embeddable_factory.ts +++ b/examples/embeddable_examples/public/migrations/migrations_embeddable_factory.ts @@ -62,7 +62,7 @@ export class SimpleEmbeddableFactoryDefinition public getDisplayName() { return i18n.translate('embeddableExamples.migrations.displayName', { - defaultMessage: 'hello world', + defaultMessage: 'simple migration embeddable', }); } } diff --git a/examples/embeddable_examples/public/plugin.ts b/examples/embeddable_examples/public/plugin.ts index 12e24526d2eb2..c94eef3107972 100644 --- a/examples/embeddable_examples/public/plugin.ts +++ b/examples/embeddable_examples/public/plugin.ts @@ -31,7 +31,8 @@ import { FilterDebuggerEmbeddableFactory, FilterDebuggerEmbeddableFactoryDefinition, } from './filter_debugger'; -import { registerMarkdownEditorEmbeddable } from './react_embeddables/eui_markdown_react_embeddable'; +import { registerMarkdownEditorEmbeddable } from './react_embeddables/eui_markdown/eui_markdown_react_embeddable'; +import { registerCreateEuiMarkdownAction } from './react_embeddables/eui_markdown/create_eui_markdown_action'; export interface EmbeddableExamplesSetupDependencies { embeddable: EmbeddableSetup; @@ -54,8 +55,6 @@ export interface EmbeddableExamplesStart { factories: ExampleEmbeddableFactories; } -registerMarkdownEditorEmbeddable(); - export class EmbeddableExamplesPlugin implements Plugin< @@ -71,6 +70,9 @@ export class EmbeddableExamplesPlugin core: CoreSetup, deps: EmbeddableExamplesSetupDependencies ) { + registerMarkdownEditorEmbeddable(); + registerCreateEuiMarkdownAction(deps.uiActions); + this.exampleEmbeddableFactories.getHelloWorldEmbeddableFactory = deps.embeddable.registerEmbeddableFactory( HELLO_WORLD_EMBEDDABLE, diff --git a/examples/embeddable_examples/public/react_embeddables/eui_markdown/constants.ts b/examples/embeddable_examples/public/react_embeddables/eui_markdown/constants.ts new file mode 100644 index 0000000000000..93227e28e211b --- /dev/null +++ b/examples/embeddable_examples/public/react_embeddables/eui_markdown/constants.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const EUI_MARKDOWN_ID = 'euiMarkdown'; +export const ADD_EUI_MARKDOWN_ACTION_ID = 'create_eui_markdown'; diff --git a/examples/embeddable_examples/public/react_embeddables/eui_markdown/create_eui_markdown_action.tsx b/examples/embeddable_examples/public/react_embeddables/eui_markdown/create_eui_markdown_action.tsx new file mode 100644 index 0000000000000..ef2bb9e5bd154 --- /dev/null +++ b/examples/embeddable_examples/public/react_embeddables/eui_markdown/create_eui_markdown_action.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { apiIsPresentationContainer } from '@kbn/presentation-containers'; +import { EmbeddableApiContext } from '@kbn/presentation-publishing'; +import { IncompatibleActionError, UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import { ADD_EUI_MARKDOWN_ACTION_ID, EUI_MARKDOWN_ID } from './constants'; + +// ----------------------------------------------------------------------------- +// Create and register an action which allows this embeddable to be created from +// the dashboard toolbar context menu. +// ----------------------------------------------------------------------------- +export const registerCreateEuiMarkdownAction = (uiActions: UiActionsStart) => { + uiActions.registerAction({ + id: ADD_EUI_MARKDOWN_ACTION_ID, + getIconType: () => 'editorCodeBlock', + isCompatible: async ({ embeddable }) => { + return apiIsPresentationContainer(embeddable); + }, + execute: async ({ embeddable }) => { + if (!apiIsPresentationContainer(embeddable)) throw new IncompatibleActionError(); + embeddable.addNewPanel( + { + panelType: EUI_MARKDOWN_ID, + initialState: { content: '# hello world!' }, + }, + true + ); + }, + getDisplayName: () => + i18n.translate('embeddableExamples.euiMarkdownEditor.ariaLabel', { + defaultMessage: 'EUI Markdown', + }), + }); + uiActions.attachAction('ADD_PANEL_TRIGGER', ADD_EUI_MARKDOWN_ACTION_ID); +}; diff --git a/examples/embeddable_examples/public/react_embeddables/eui_markdown/eui_markdown_react_embeddable.tsx b/examples/embeddable_examples/public/react_embeddables/eui_markdown/eui_markdown_react_embeddable.tsx new file mode 100644 index 0000000000000..c699ca5799967 --- /dev/null +++ b/examples/embeddable_examples/public/react_embeddables/eui_markdown/eui_markdown_react_embeddable.tsx @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EuiMarkdownEditor, EuiMarkdownFormat } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { + initializeReactEmbeddableTitles, + initializeReactEmbeddableUuid, + ReactEmbeddableFactory, + RegisterReactEmbeddable, + registerReactEmbeddableFactory, + useReactEmbeddableApiHandle, + useReactEmbeddableUnsavedChanges, +} from '@kbn/embeddable-plugin/public'; +import { i18n } from '@kbn/i18n'; +import { useInheritedViewMode, useStateFromPublishingSubject } from '@kbn/presentation-publishing'; +import { euiThemeVars } from '@kbn/ui-theme'; +import React from 'react'; +import { BehaviorSubject } from 'rxjs'; +import { EUI_MARKDOWN_ID } from './constants'; +import { MarkdownEditorSerializedState, MarkdownEditorApi } from './types'; + +export const registerMarkdownEditorEmbeddable = () => { + const markdownEmbeddableFactory: ReactEmbeddableFactory< + MarkdownEditorSerializedState, + MarkdownEditorApi + > = { + deserializeState: (state) => { + /** + * Here we can run migrations and inject references. + */ + return state.rawState as MarkdownEditorSerializedState; + }, + getComponent: async (state, maybeId) => { + /** + * initialize state (source of truth) + */ + const uuid = initializeReactEmbeddableUuid(maybeId); + const { titlesApi, titleComparators, serializeTitles } = + initializeReactEmbeddableTitles(state); + const contentSubject = new BehaviorSubject(state.content); + + /** + * getComponent is async so you can async import the component or load a saved object here. + * the loading will be handed gracefully by the Presentation Container. + */ + + return RegisterReactEmbeddable((apiRef) => { + /** + * Unsaved changes logic is handled automatically by this hook. You only need to provide + * a subject, setter, and optional state comparator for each key in your state type. + */ + const { unsavedChanges, resetUnsavedChanges } = useReactEmbeddableUnsavedChanges( + uuid, + markdownEmbeddableFactory, + { + content: [contentSubject, (value) => contentSubject.next(value)], + ...titleComparators, + } + ); + + /** + * Publish the API. This is what gets forwarded to the Actions framework, and to whatever the + * parent of this embeddable is. + */ + const thisApi = useReactEmbeddableApiHandle( + { + ...titlesApi, + unsavedChanges, + resetUnsavedChanges, + serializeState: async () => { + return { + rawState: { + ...serializeTitles(), + content: contentSubject.getValue(), + }, + }; + }, + }, + apiRef, + uuid + ); + + // get state for rendering + const content = useStateFromPublishingSubject(contentSubject); + const viewMode = useInheritedViewMode(thisApi) ?? 'view'; + + return viewMode === 'edit' ? ( + contentSubject.next(value)} + aria-label={i18n.translate('embeddableExamples.euiMarkdownEditor.ariaLabel', { + defaultMessage: 'Dashboard markdown editor', + })} + height="full" + /> + ) : ( + + {content ?? ''} + + ); + }); + }, + }; + + /** + * Register the defined Embeddable Factory - notice that this isn't defined + * on the plugin. Instead, it's a simple imported function. I.E to register an + * embeddable, you only need the embeddable plugin in your requiredBundles + */ + registerReactEmbeddableFactory(EUI_MARKDOWN_ID, markdownEmbeddableFactory); +}; diff --git a/examples/embeddable_examples/public/react_embeddables/eui_markdown/types.ts b/examples/embeddable_examples/public/react_embeddables/eui_markdown/types.ts new file mode 100644 index 0000000000000..1e594ff61ba68 --- /dev/null +++ b/examples/embeddable_examples/public/react_embeddables/eui_markdown/types.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + DefaultEmbeddableApi, + SerializedReactEmbeddableTitles, +} from '@kbn/embeddable-plugin/public'; + +export type MarkdownEditorSerializedState = SerializedReactEmbeddableTitles & { + content: string; +}; + +export type MarkdownEditorApi = DefaultEmbeddableApi; diff --git a/examples/embeddable_examples/public/react_embeddables/eui_markdown_react_embeddable.tsx b/examples/embeddable_examples/public/react_embeddables/eui_markdown_react_embeddable.tsx deleted file mode 100644 index b525181f307a9..0000000000000 --- a/examples/embeddable_examples/public/react_embeddables/eui_markdown_react_embeddable.tsx +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { EuiMarkdownEditor, EuiMarkdownFormat } from '@elastic/eui'; -import { css } from '@emotion/react'; -import { euiThemeVars } from '@kbn/ui-theme'; -import { - ReactEmbeddableFactory, - RegisterReactEmbeddable, - registerReactEmbeddableFactory, - useReactEmbeddableApiHandle, - initializeReactEmbeddableUuid, - initializeReactEmbeddableTitles, - SerializedReactEmbeddableTitles, - DefaultEmbeddableApi, - useReactEmbeddableUnsavedChanges, -} from '@kbn/embeddable-plugin/public'; -import { i18n } from '@kbn/i18n'; -import { useInheritedViewMode, useStateFromPublishingSubject } from '@kbn/presentation-publishing'; -import React from 'react'; -import { BehaviorSubject } from 'rxjs'; - -// ----------------------------------------------------------------------------- -// Types for this embeddable -// ----------------------------------------------------------------------------- -type MarkdownEditorSerializedState = SerializedReactEmbeddableTitles & { - content: string; -}; - -type MarkdownEditorApi = DefaultEmbeddableApi; - -const type = 'euiMarkdown'; - -// ----------------------------------------------------------------------------- -// Define the Embeddable Factory -// ----------------------------------------------------------------------------- -const markdownEmbeddableFactory: ReactEmbeddableFactory< - MarkdownEditorSerializedState, - MarkdownEditorApi -> = { - // ----------------------------------------------------------------------------- - // Deserialize function - // ----------------------------------------------------------------------------- - deserializeState: (state) => { - // We could run migrations here. - // We should inject references here. References are given as state.references - - return state.rawState as MarkdownEditorSerializedState; - }, - - // ----------------------------------------------------------------------------- - // Register the Embeddable component - // ----------------------------------------------------------------------------- - getComponent: async (state, maybeId) => { - /** - * initialize state (source of truth) - */ - const uuid = initializeReactEmbeddableUuid(maybeId); - const { titlesApi, titleComparators, serializeTitles } = initializeReactEmbeddableTitles(state); - const contentSubject = new BehaviorSubject(state.content); - - /** - * getComponent is async so you can async import the component or load a saved object here. - * the loading will be handed gracefully by the Presentation Container. - */ - - return RegisterReactEmbeddable((apiRef) => { - /** - * Unsaved changes logic is handled automatically by this hook. You only need to provide - * a subject, setter, and optional state comparator for each key in your state type. - */ - const { unsavedChanges, resetUnsavedChanges } = useReactEmbeddableUnsavedChanges( - uuid, - markdownEmbeddableFactory, - { - content: [contentSubject, (value) => contentSubject.next(value)], - ...titleComparators, - } - ); - - /** - * Publish the API. This is what gets forwarded to the Actions framework, and to whatever the - * parent of this embeddable is. - */ - const thisApi = useReactEmbeddableApiHandle( - { - ...titlesApi, - unsavedChanges, - resetUnsavedChanges, - serializeState: async () => { - return { - rawState: { - ...serializeTitles(), - content: contentSubject.getValue(), - }, - }; - }, - }, - apiRef, - uuid - ); - - // get state for rendering - const content = useStateFromPublishingSubject(contentSubject); - const viewMode = useInheritedViewMode(thisApi) ?? 'view'; - - return viewMode === 'edit' ? ( - contentSubject.next(value)} - aria-label={i18n.translate('dashboard.test.markdownEditor.ariaLabel', { - defaultMessage: 'Dashboard markdown editor', - })} - height="full" - /> - ) : ( - - {content ?? ''} - - ); - }); - }, -}; - -// ----------------------------------------------------------------------------- -// Register the defined Embeddable Factory - notice that this isn't defined -// on the plugin. Instead, it's a simple imported function. I.E to register an -// Embeddable, you only need the embeddable plugin in your requiredBundles -// ----------------------------------------------------------------------------- -export const registerMarkdownEditorEmbeddable = () => - registerReactEmbeddableFactory(type, markdownEmbeddableFactory); diff --git a/examples/embeddable_examples/tsconfig.json b/examples/embeddable_examples/tsconfig.json index 0f22cccc94483..35f799c8c4e3a 100644 --- a/examples/embeddable_examples/tsconfig.json +++ b/examples/embeddable_examples/tsconfig.json @@ -20,6 +20,7 @@ "@kbn/presentation-publishing", "@kbn/ui-theme", "@kbn/i18n", - "@kbn/es-query" + "@kbn/es-query", + "@kbn/presentation-containers" ] } diff --git a/fleet_packages.json b/fleet_packages.json index c21024e7e6688..79722187a1b0d 100644 --- a/fleet_packages.json +++ b/fleet_packages.json @@ -24,7 +24,7 @@ [ { "name": "apm", - "version": "8.13.0-preview-1705349439", + "version": "8.13.0-preview-1705349440", "forceAlignStackVersion": true, "allowSyncToPrerelease": true }, diff --git a/package.json b/package.json index 3bcd5075747ea..ccd5182e04a55 100644 --- a/package.json +++ b/package.json @@ -376,6 +376,7 @@ "@kbn/data-view-field-editor-example-plugin": "link:examples/data_view_field_editor_example", "@kbn/data-view-field-editor-plugin": "link:src/plugins/data_view_field_editor", "@kbn/data-view-management-plugin": "link:src/plugins/data_view_management", + "@kbn/data-view-utils": "link:packages/kbn-data-view-utils", "@kbn/data-views-plugin": "link:src/plugins/data_views", "@kbn/data-visualizer-plugin": "link:x-pack/plugins/data_visualizer", "@kbn/dataset-quality-plugin": "link:x-pack/plugins/dataset_quality", @@ -1064,7 +1065,6 @@ "react-popper-tooltip": "^3.1.1", "react-redux": "^7.2.8", "react-resizable": "^3.0.4", - "react-resize-detector": "^7.1.1", "react-reverse-portal": "^2.1.0", "react-router": "^5.3.4", "react-router-config": "^5.1.1", diff --git a/packages/analytics/shippers/fullstory/src/fullstory_shipper.ts b/packages/analytics/shippers/fullstory/src/fullstory_shipper.ts index a4ad730f87b2e..d02e87cace6b1 100644 --- a/packages/analytics/shippers/fullstory/src/fullstory_shipper.ts +++ b/packages/analytics/shippers/fullstory/src/fullstory_shipper.ts @@ -30,7 +30,7 @@ const PAGE_VARS_KEYS = [ // Deployment-specific keys 'version', // x4, split to version_major, version_minor, version_patch for easier filtering - 'buildNum', // May be useful for Serverless + 'buildNum', // May be useful for Serverless, TODO: replace with buildHash 'cloudId', 'deploymentId', 'projectId', // projectId and deploymentId are mutually exclusive. They shouldn't be sent in the same offering. diff --git a/packages/core/apps/core-apps-server-internal/src/bundle_routes/register_bundle_routes.test.ts b/packages/core/apps/core-apps-server-internal/src/bundle_routes/register_bundle_routes.test.ts index e6550f6e86cb6..8a0ae599150fd 100644 --- a/packages/core/apps/core-apps-server-internal/src/bundle_routes/register_bundle_routes.test.ts +++ b/packages/core/apps/core-apps-server-internal/src/bundle_routes/register_bundle_routes.test.ts @@ -13,10 +13,12 @@ import { httpServiceMock } from '@kbn/core-http-server-mocks'; import type { InternalPluginInfo, UiPlugins } from '@kbn/core-plugins-base-server-internal'; import { registerBundleRoutes } from './register_bundle_routes'; import { FileHashCache } from './file_hash_cache'; +import { BasePath, StaticAssets } from '@kbn/core-http-server-internal'; const createPackageInfo = (parts: Partial = {}): PackageInfo => ({ buildNum: 42, - buildSha: 'sha', + buildSha: 'shasha', + buildShaShort: 'sha', dist: true, branch: 'master', version: '8.0.0', @@ -41,9 +43,12 @@ const createUiPlugins = (...ids: string[]): UiPlugins => ({ describe('registerBundleRoutes', () => { let router: ReturnType; + let staticAssets: StaticAssets; beforeEach(() => { router = httpServiceMock.createRouter(); + const basePath = httpServiceMock.createBasePath('/server-base-path') as unknown as BasePath; + staticAssets = new StaticAssets({ basePath, cdnConfig: {} as any, shaDigest: 'sha' }); }); afterEach(() => { @@ -53,7 +58,7 @@ describe('registerBundleRoutes', () => { it('registers core and shared-dep bundles', () => { registerBundleRoutes({ router, - serverBasePath: '/server-base-path', + staticAssets, packageInfo: createPackageInfo(), uiPlugins: createUiPlugins(), }); @@ -64,39 +69,39 @@ describe('registerBundleRoutes', () => { fileHashCache: expect.any(FileHashCache), isDist: true, bundlesPath: 'uiSharedDepsSrcDistDir', - publicPath: '/server-base-path/42/bundles/kbn-ui-shared-deps-src/', - routePath: '/42/bundles/kbn-ui-shared-deps-src/', + publicPath: '/server-base-path/sha/bundles/kbn-ui-shared-deps-src/', + routePath: '/sha/bundles/kbn-ui-shared-deps-src/', }); expect(registerRouteForBundleMock).toHaveBeenCalledWith(router, { fileHashCache: expect.any(FileHashCache), isDist: true, bundlesPath: 'uiSharedDepsNpmDistDir', - publicPath: '/server-base-path/42/bundles/kbn-ui-shared-deps-npm/', - routePath: '/42/bundles/kbn-ui-shared-deps-npm/', + publicPath: '/server-base-path/sha/bundles/kbn-ui-shared-deps-npm/', + routePath: '/sha/bundles/kbn-ui-shared-deps-npm/', }); expect(registerRouteForBundleMock).toHaveBeenCalledWith(router, { fileHashCache: expect.any(FileHashCache), isDist: true, bundlesPath: expect.stringMatching(/\/@kbn\/core\/target\/public$/), - publicPath: '/server-base-path/42/bundles/core/', - routePath: '/42/bundles/core/', + publicPath: '/server-base-path/sha/bundles/core/', + routePath: '/sha/bundles/core/', }); expect(registerRouteForBundleMock).toHaveBeenCalledWith(router, { fileHashCache: expect.any(FileHashCache), isDist: true, bundlesPath: 'kbnMonacoBundleDir', - publicPath: '/server-base-path/42/bundles/kbn-monaco/', - routePath: '/42/bundles/kbn-monaco/', + publicPath: '/server-base-path/sha/bundles/kbn-monaco/', + routePath: '/sha/bundles/kbn-monaco/', }); }); it('registers plugin bundles', () => { registerBundleRoutes({ router, - serverBasePath: '/server-base-path', + staticAssets, packageInfo: createPackageInfo(), uiPlugins: createUiPlugins('plugin-a', 'plugin-b'), }); @@ -107,16 +112,16 @@ describe('registerBundleRoutes', () => { fileHashCache: expect.any(FileHashCache), isDist: true, bundlesPath: '/plugins/plugin-a/public-target-dir', - publicPath: '/server-base-path/42/bundles/plugin/plugin-a/8.0.0/', - routePath: '/42/bundles/plugin/plugin-a/8.0.0/', + publicPath: '/server-base-path/sha/bundles/plugin/plugin-a/8.0.0/', + routePath: '/sha/bundles/plugin/plugin-a/8.0.0/', }); expect(registerRouteForBundleMock).toHaveBeenCalledWith(router, { fileHashCache: expect.any(FileHashCache), isDist: true, bundlesPath: '/plugins/plugin-b/public-target-dir', - publicPath: '/server-base-path/42/bundles/plugin/plugin-b/8.0.0/', - routePath: '/42/bundles/plugin/plugin-b/8.0.0/', + publicPath: '/server-base-path/sha/bundles/plugin/plugin-b/8.0.0/', + routePath: '/sha/bundles/plugin/plugin-b/8.0.0/', }); }); }); diff --git a/packages/core/apps/core-apps-server-internal/src/bundle_routes/register_bundle_routes.ts b/packages/core/apps/core-apps-server-internal/src/bundle_routes/register_bundle_routes.ts index 22266e97355e3..617f085c8ad43 100644 --- a/packages/core/apps/core-apps-server-internal/src/bundle_routes/register_bundle_routes.ts +++ b/packages/core/apps/core-apps-server-internal/src/bundle_routes/register_bundle_routes.ts @@ -13,6 +13,7 @@ import { distDir as UiSharedDepsSrcDistDir } from '@kbn/ui-shared-deps-src'; import * as KbnMonaco from '@kbn/monaco/server'; import type { IRouter } from '@kbn/core-http-server'; import type { UiPlugins } from '@kbn/core-plugins-base-server-internal'; +import { InternalStaticAssets } from '@kbn/core-http-server-internal'; import { FileHashCache } from './file_hash_cache'; import { registerRouteForBundle } from './bundles_route'; @@ -28,56 +29,61 @@ import { registerRouteForBundle } from './bundles_route'; */ export function registerBundleRoutes({ router, - serverBasePath, uiPlugins, packageInfo, + staticAssets, }: { router: IRouter; - serverBasePath: string; uiPlugins: UiPlugins; packageInfo: PackageInfo; + staticAssets: InternalStaticAssets; }) { - const { dist: isDist, buildNum } = packageInfo; + const { dist: isDist } = packageInfo; // rather than calculate the fileHash on every request, we // provide a cache object to `resolveDynamicAssetResponse()` that // will store the most recently used hashes. const fileHashCache = new FileHashCache(); + const sharedNpmDepsPath = '/bundles/kbn-ui-shared-deps-npm/'; registerRouteForBundle(router, { - publicPath: `${serverBasePath}/${buildNum}/bundles/kbn-ui-shared-deps-npm/`, - routePath: `/${buildNum}/bundles/kbn-ui-shared-deps-npm/`, + publicPath: staticAssets.prependPublicUrl(sharedNpmDepsPath) + '/', + routePath: staticAssets.prependServerPath(sharedNpmDepsPath) + '/', bundlesPath: UiSharedDepsNpm.distDir, fileHashCache, isDist, }); + const sharedDepsPath = '/bundles/kbn-ui-shared-deps-src/'; registerRouteForBundle(router, { - publicPath: `${serverBasePath}/${buildNum}/bundles/kbn-ui-shared-deps-src/`, - routePath: `/${buildNum}/bundles/kbn-ui-shared-deps-src/`, + publicPath: staticAssets.prependPublicUrl(sharedDepsPath) + '/', + routePath: staticAssets.prependServerPath(sharedDepsPath) + '/', bundlesPath: UiSharedDepsSrcDistDir, fileHashCache, isDist, }); + const coreBundlePath = '/bundles/core/'; registerRouteForBundle(router, { - publicPath: `${serverBasePath}/${buildNum}/bundles/core/`, - routePath: `/${buildNum}/bundles/core/`, + publicPath: staticAssets.prependPublicUrl(coreBundlePath) + '/', + routePath: staticAssets.prependServerPath(coreBundlePath) + '/', bundlesPath: isDist ? fromRoot('node_modules/@kbn/core/target/public') : fromRoot('src/core/target/public'), fileHashCache, isDist, }); + const monacoEditorPath = '/bundles/kbn-monaco/'; registerRouteForBundle(router, { - publicPath: `${serverBasePath}/${buildNum}/bundles/kbn-monaco/`, - routePath: `/${buildNum}/bundles/kbn-monaco/`, + publicPath: staticAssets.prependPublicUrl(monacoEditorPath) + '/', + routePath: staticAssets.prependServerPath(monacoEditorPath) + '/', bundlesPath: KbnMonaco.bundleDir, fileHashCache, isDist, }); [...uiPlugins.internal.entries()].forEach(([id, { publicTargetDir, version }]) => { + const pluginBundlesPath = `/bundles/plugin/${id}/${version}/`; registerRouteForBundle(router, { - publicPath: `${serverBasePath}/${buildNum}/bundles/plugin/${id}/${version}/`, - routePath: `/${buildNum}/bundles/plugin/${id}/${version}/`, + publicPath: staticAssets.prependPublicUrl(pluginBundlesPath) + '/', + routePath: staticAssets.prependServerPath(pluginBundlesPath) + '/', bundlesPath: publicTargetDir, fileHashCache, isDist, diff --git a/packages/core/apps/core-apps-server-internal/src/core_app.test.ts b/packages/core/apps/core-apps-server-internal/src/core_app.test.ts index 851f443cd3e1c..f0bde326b7b74 100644 --- a/packages/core/apps/core-apps-server-internal/src/core_app.test.ts +++ b/packages/core/apps/core-apps-server-internal/src/core_app.test.ts @@ -16,8 +16,8 @@ import { httpResourcesMock } from '@kbn/core-http-resources-server-mocks'; import { PluginType } from '@kbn/core-base-common'; import type { RequestHandlerContext } from '@kbn/core-http-request-handler-context-server'; import { coreInternalLifecycleMock } from '@kbn/core-lifecycle-server-mocks'; -import { CoreAppsService } from './core_app'; import { of } from 'rxjs'; +import { CoreAppsService } from './core_app'; const emptyPlugins = (): UiPlugins => ({ internal: new Map(), @@ -146,7 +146,7 @@ describe('CoreApp', () => { uiPlugins: prebootUIPlugins, router: expect.any(Object), packageInfo: coreContext.env.packageInfo, - serverBasePath: internalCorePreboot.http.basePath.serverBasePath, + staticAssets: expect.any(Object), }); }); @@ -245,7 +245,23 @@ describe('CoreApp', () => { uiPlugins, router: expect.any(Object), packageInfo: coreContext.env.packageInfo, - serverBasePath: internalCoreSetup.http.basePath.serverBasePath, + staticAssets: expect.any(Object), }); }); + + it('registers SHA-scoped and non-SHA-scoped UI bundle routes', async () => { + const uiPlugins = emptyPlugins(); + internalCoreSetup.http.staticAssets.prependServerPath.mockReturnValue('/some-path'); + await coreApp.setup(internalCoreSetup, uiPlugins); + + expect(internalCoreSetup.http.registerStaticDir).toHaveBeenCalledTimes(2); + expect(internalCoreSetup.http.registerStaticDir).toHaveBeenCalledWith( + '/some-path', + expect.any(String) + ); + expect(internalCoreSetup.http.registerStaticDir).toHaveBeenCalledWith( + '/ui/{path*}', + expect.any(String) + ); + }); }); diff --git a/packages/core/apps/core-apps-server-internal/src/core_app.ts b/packages/core/apps/core-apps-server-internal/src/core_app.ts index 3de295874d3fe..1e54d7d8aaa26 100644 --- a/packages/core/apps/core-apps-server-internal/src/core_app.ts +++ b/packages/core/apps/core-apps-server-internal/src/core_app.ts @@ -22,6 +22,7 @@ import type { import type { UiPlugins } from '@kbn/core-plugins-base-server-internal'; import type { HttpResources, HttpResourcesServiceToolkit } from '@kbn/core-http-resources-server'; import type { InternalCorePreboot, InternalCoreSetup } from '@kbn/core-lifecycle-server-internal'; +import type { InternalStaticAssets } from '@kbn/core-http-server-internal'; import { firstValueFrom, map, type Observable } from 'rxjs'; import { CoreAppConfig, type CoreAppConfigType, CoreAppPath } from './core_app_config'; import { registerBundleRoutes } from './bundle_routes'; @@ -33,6 +34,7 @@ interface CommonRoutesParams { httpResources: HttpResources; basePath: IBasePath; uiPlugins: UiPlugins; + staticAssets: InternalStaticAssets; onResourceNotFound: ( req: KibanaRequest, res: HttpResourcesServiceToolkit & KibanaResponseFactory @@ -77,10 +79,11 @@ export class CoreAppsService { this.registerCommonDefaultRoutes({ basePath: corePreboot.http.basePath, httpResources: corePreboot.httpResources.createRegistrar(router), + staticAssets: corePreboot.http.staticAssets, router, uiPlugins, onResourceNotFound: async (req, res) => - // THe API consumers might call various Kibana APIs (e.g. `/api/status`) when Kibana is still at the preboot + // The API consumers might call various Kibana APIs (e.g. `/api/status`) when Kibana is still at the preboot // stage, and the main HTTP server that registers API handlers isn't up yet. At this stage we don't know if // the API endpoint exists or not, and hence cannot reply with `404`. We also should not reply with completely // unexpected response (`200 text/html` for the Core app). The only suitable option is to reply with `503` @@ -125,6 +128,7 @@ export class CoreAppsService { this.registerCommonDefaultRoutes({ basePath: coreSetup.http.basePath, httpResources: resources, + staticAssets: coreSetup.http.staticAssets, router, uiPlugins, onResourceNotFound: async (req, res) => res.notFound(), @@ -210,6 +214,7 @@ export class CoreAppsService { private registerCommonDefaultRoutes({ router, basePath, + staticAssets, uiPlugins, onResourceNotFound, httpResources, @@ -259,17 +264,23 @@ export class CoreAppsService { registerBundleRoutes({ router, uiPlugins, + staticAssets, packageInfo: this.env.packageInfo, - serverBasePath: basePath.serverBasePath, }); } // After the package is built and bootstrap extracts files to bazel-bin, // assets are exposed at the root of the package and in the package's node_modules dir private registerStaticDirs(core: InternalCoreSetup | InternalCorePreboot) { - core.http.registerStaticDir( - '/ui/{path*}', - fromRoot('node_modules/@kbn/core-apps-server-internal/assets') - ); + /** + * Serve UI from sha-scoped and not-sha-scoped paths to allow time for plugin code to migrate + * Eventually we only want to serve from the sha scoped path + */ + [core.http.staticAssets.prependServerPath('/ui/{path*}'), '/ui/{path*}'].forEach((path) => { + core.http.registerStaticDir( + path, + fromRoot('node_modules/@kbn/core-apps-server-internal/assets') + ); + }); } } diff --git a/packages/core/apps/core-apps-server-internal/tsconfig.json b/packages/core/apps/core-apps-server-internal/tsconfig.json index 36ecc68c7cbc1..fc8aa9f25349c 100644 --- a/packages/core/apps/core-apps-server-internal/tsconfig.json +++ b/packages/core/apps/core-apps-server-internal/tsconfig.json @@ -32,6 +32,7 @@ "@kbn/core-lifecycle-server-mocks", "@kbn/core-ui-settings-server", "@kbn/monaco", + "@kbn/core-http-server-internal", ], "exclude": [ "target/**/*", diff --git a/packages/core/base/core-base-browser-mocks/src/core_context.mock.ts b/packages/core/base/core-base-browser-mocks/src/core_context.mock.ts index 53933d4146df3..cdbafc09c5c03 100644 --- a/packages/core/base/core-base-browser-mocks/src/core_context.mock.ts +++ b/packages/core/base/core-base-browser-mocks/src/core_context.mock.ts @@ -24,6 +24,7 @@ function createCoreContext({ production = false }: { production?: boolean } = {} branch: 'branch', buildNum: 100, buildSha: 'buildSha', + buildShaShort: 'buildShaShort', dist: false, buildDate: new Date('2023-05-15T23:12:09.000Z'), buildFlavor: 'traditional', diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_action_menu.test.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_action_menu.test.tsx index 9a7b4fa5a8b14..359439f3e4cdf 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_action_menu.test.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_action_menu.test.tsx @@ -147,4 +147,24 @@ describe('HeaderActionMenu', () => { expect(unmounts.FOO).toHaveBeenCalledTimes(1); expect(unmounts.BAR).not.toHaveBeenCalled(); }); + + it('calls mount point `unmount` when unmounts', () => { + const TestComponent = () => { + const mounter = useHeaderActionMenuMounter(menuMount$); + return ; + }; + component = mount(); + + act(() => { + menuMount$.next(createMountPoint('FOO')); + }); + refresh(); + + expect(Object.keys(unmounts)).toEqual(['FOO']); + expect(unmounts.FOO).not.toHaveBeenCalled(); + + component.unmount(); + + expect(unmounts.FOO).toHaveBeenCalledTimes(1); + }); }); diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_action_menu.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_action_menu.tsx index ec2a06266a0fe..91d2077b28ea9 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_action_menu.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_action_menu.tsx @@ -39,11 +39,6 @@ export const HeaderActionMenu: FC = ({ mounter }) => { const unmountRef = useRef(null); useLayoutEffect(() => { - if (unmountRef.current) { - unmountRef.current(); - unmountRef.current = null; - } - if (mounter.mount && elementRef.current) { try { unmountRef.current = mounter.mount(elementRef.current); @@ -53,6 +48,12 @@ export const HeaderActionMenu: FC = ({ mounter }) => { console.error(e); } } + return () => { + if (unmountRef.current) { + unmountRef.current(); + unmountRef.current = null; + } + }; }, [mounter]); return
; diff --git a/packages/core/http/core-http-server-internal/index.ts b/packages/core/http/core-http-server-internal/index.ts index 2867b5d2a0317..b9d4edd8a8628 100644 --- a/packages/core/http/core-http-server-internal/index.ts +++ b/packages/core/http/core-http-server-internal/index.ts @@ -17,6 +17,7 @@ export type { InternalHttpServiceStart, } from './src/types'; export { BasePath } from './src/base_path_service'; +export { type InternalStaticAssets, StaticAssets } from './src/static_assets'; export { cspConfig, CspConfig, type CspConfigType } from './src/csp'; diff --git a/packages/core/http/core-http-server-internal/src/cdn.test.ts b/packages/core/http/core-http-server-internal/src/cdn_config.test.ts similarity index 98% rename from packages/core/http/core-http-server-internal/src/cdn.test.ts rename to packages/core/http/core-http-server-internal/src/cdn_config.test.ts index 74f165bfd0f22..b6a954782f523 100644 --- a/packages/core/http/core-http-server-internal/src/cdn.test.ts +++ b/packages/core/http/core-http-server-internal/src/cdn_config.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { CdnConfig } from './cdn'; +import { CdnConfig } from './cdn_config'; describe('CdnConfig', () => { it.each([ diff --git a/packages/core/http/core-http-server-internal/src/cdn.ts b/packages/core/http/core-http-server-internal/src/cdn_config.ts similarity index 90% rename from packages/core/http/core-http-server-internal/src/cdn.ts rename to packages/core/http/core-http-server-internal/src/cdn_config.ts index 0f9b386b09237..e6fa29200ac74 100644 --- a/packages/core/http/core-http-server-internal/src/cdn.ts +++ b/packages/core/http/core-http-server-internal/src/cdn_config.ts @@ -14,15 +14,15 @@ export interface Input { } export class CdnConfig { - private url: undefined | URL; + private readonly url: undefined | URL; constructor(url?: string) { if (url) { - this.url = new URL(url); // This will throw for invalid URLs + this.url = new URL(url); // This will throw for invalid URLs, although should be validated before reaching this point } } public get host(): undefined | string { - return this.url?.host ?? undefined; + return this.url?.host; } public get baseHref(): undefined | string { diff --git a/packages/core/http/core-http-server-internal/src/http_config.test.ts b/packages/core/http/core-http-server-internal/src/http_config.test.ts index 28abe6513ced6..f0bd5773b40b9 100644 --- a/packages/core/http/core-http-server-internal/src/http_config.test.ts +++ b/packages/core/http/core-http-server-internal/src/http_config.test.ts @@ -16,8 +16,8 @@ const invalidHostnames = ['asdf$%^', '0']; let mockHostname = 'kibana-hostname'; -jest.mock('os', () => { - const original = jest.requireActual('os'); +jest.mock('node:os', () => { + const original = jest.requireActual('node:os'); return { ...original, @@ -530,6 +530,29 @@ describe('restrictInternalApis', () => { }); }); +describe('cdn', () => { + it('allows correct URL', () => { + expect(config.schema.validate({ cdn: { url: 'https://cdn.example.com' } })).toMatchObject({ + cdn: { url: 'https://cdn.example.com' }, + }); + }); + it.each([['foo'], ['http:./']])('throws for invalid URL %s', (url) => { + expect(() => config.schema.validate({ cdn: { url } })).toThrowErrorMatchingInlineSnapshot( + `"[cdn.url]: expected URI with scheme [http|https]."` + ); + }); + it.each([ + ['https://cdn.example.com:1234/asd?thing=1', 'URL query string not allowed'], + ['https://cdn.example.com:1234/asd#cool', 'URL fragment not allowed'], + [ + 'https://cdn.example.com:1234/asd?thing=1#cool', + 'URL fragment not allowed, but found "#cool"\nURL query string not allowed, but found "?thing=1"', + ], + ])('throws for disallowed values %s', (url, expecterError) => { + expect(() => config.schema.validate({ cdn: { url } })).toThrow(expecterError); + }); +}); + describe('HttpConfig', () => { it('converts customResponseHeaders to strings or arrays of strings', () => { const httpSchema = config.schema; diff --git a/packages/core/http/core-http-server-internal/src/http_config.ts b/packages/core/http/core-http-server-internal/src/http_config.ts index f6880c38e49ba..54b8e808f675b 100644 --- a/packages/core/http/core-http-server-internal/src/http_config.ts +++ b/packages/core/http/core-http-server-internal/src/http_config.ts @@ -12,8 +12,8 @@ import type { ServiceConfigDescriptor } from '@kbn/core-base-server-internal'; import { uuidRegexp } from '@kbn/core-base-server-internal'; import type { ICspConfig, IExternalUrlConfig } from '@kbn/core-http-server'; -import { hostname } from 'os'; -import url from 'url'; +import { hostname, EOL } from 'node:os'; +import url, { URL } from 'node:url'; import type { Duration } from 'moment'; import type { IHttpEluMonitorConfig } from '@kbn/core-http-server/src/elu_monitor'; @@ -24,7 +24,7 @@ import { securityResponseHeadersSchema, parseRawSecurityResponseHeadersConfig, } from './security_response_headers_config'; -import { CdnConfig } from './cdn'; +import { CdnConfig } from './cdn_config'; const validBasePathRegex = /^\/.*[^\/]$/; @@ -40,6 +40,24 @@ const validHostName = () => { return hostname().replace(/[^\x00-\x7F]/g, ''); }; +/** + * We assume the URL does not contain anything after the pathname so that + * we can safely append values to the pathname at runtime. + */ +function validateCdnURL(urlString: string): undefined | string { + const cdnURL = new URL(urlString); + const errors: string[] = []; + if (cdnURL.hash.length) { + errors.push(`URL fragment not allowed, but found "${cdnURL.hash}"`); + } + if (cdnURL.search.length) { + errors.push(`URL query string not allowed, but found "${cdnURL.search}"`); + } + if (errors.length) { + return `CDN URL "${cdnURL.href}" is invalid:${EOL}${errors.join(EOL)}`; + } +} + const configSchema = schema.object( { name: schema.string({ defaultValue: () => validHostName() }), @@ -60,7 +78,7 @@ const configSchema = schema.object( }, }), cdn: schema.object({ - url: schema.maybe(schema.uri({ scheme: ['http', 'https'] })), + url: schema.maybe(schema.uri({ scheme: ['http', 'https'], validate: validateCdnURL })), }), cors: schema.object( { diff --git a/packages/core/http/core-http-server-internal/src/http_server.test.ts b/packages/core/http/core-http-server-internal/src/http_server.test.ts index 8f5ae22612b45..129efea82cc8c 100644 --- a/packages/core/http/core-http-server-internal/src/http_server.test.ts +++ b/packages/core/http/core-http-server-internal/src/http_server.test.ts @@ -30,6 +30,7 @@ import { Readable } from 'stream'; import { KBN_CERT_PATH, KBN_KEY_PATH } from '@kbn/dev-utils'; import moment from 'moment'; import { of, Observable, BehaviorSubject } from 'rxjs'; +import { mockCoreContext } from '@kbn/core-base-server-mocks'; const routerOptions: RouterOptions = { isDev: false, @@ -54,8 +55,9 @@ let config$: Observable; let configWithSSL: HttpConfig; let configWithSSL$: Observable; -const loggingService = loggingSystemMock.create(); -const logger = loggingService.get(); +const coreContext = mockCoreContext.create(); +const loggingService = coreContext.logger; +const logger = coreContext.logger.get(); const enhanceWithContext = (fn: (...args: any[]) => any) => fn.bind(null, {}); let certificate: string; @@ -99,7 +101,7 @@ beforeEach(() => { } as HttpConfig; configWithSSL$ = of(configWithSSL); - server = new HttpServer(loggingService, 'tests', of(config.shutdownTimeout)); + server = new HttpServer(coreContext, 'tests', of(config.shutdownTimeout)); }); afterEach(async () => { diff --git a/packages/core/http/core-http-server-internal/src/http_server.ts b/packages/core/http/core-http-server-internal/src/http_server.ts index 1361c64bb67ce..ae9025d5cd9a7 100644 --- a/packages/core/http/core-http-server-internal/src/http_server.ts +++ b/packages/core/http/core-http-server-internal/src/http_server.ts @@ -48,6 +48,8 @@ import { performance } from 'perf_hooks'; import { isBoom } from '@hapi/boom'; import { identity } from 'lodash'; import { IHttpEluMonitorConfig } from '@kbn/core-http-server/src/elu_monitor'; +import { Env } from '@kbn/config'; +import { CoreContext } from '@kbn/core-base-server-internal'; import { HttpConfig } from './http_config'; import { adoptToHapiAuthFormat } from './lifecycle/auth'; import { adoptToHapiOnPreAuth } from './lifecycle/on_pre_auth'; @@ -178,15 +180,20 @@ export class HttpServer { private stopped = false; private readonly log: Logger; + private readonly logger: LoggerFactory; private readonly authState: AuthStateStorage; private readonly authRequestHeaders: AuthHeadersStorage; private readonly authResponseHeaders: AuthHeadersStorage; + private readonly env: Env; constructor( - private readonly logger: LoggerFactory, + private readonly coreContext: CoreContext, private readonly name: string, private readonly shutdownTimeout$: Observable ) { + const { logger, env } = this.coreContext; + this.logger = logger; + this.env = env; this.authState = new AuthStateStorage(() => this.authRegistered); this.authRequestHeaders = new AuthHeadersStorage(); this.authResponseHeaders = new AuthHeadersStorage(); @@ -269,7 +276,11 @@ export class HttpServer { this.setupResponseLogging(); this.setupGracefulShutdownHandlers(); - const staticAssets = new StaticAssets(basePathService, config.cdn); + const staticAssets = new StaticAssets({ + basePath: basePathService, + cdnConfig: config.cdn, + shaDigest: this.env.packageInfo.buildShaShort, + }); return { registerRouter: this.registerRouter.bind(this), diff --git a/packages/core/http/core-http-server-internal/src/http_service.ts b/packages/core/http/core-http-server-internal/src/http_service.ts index 1ce15eb06b231..3dcab5be510e9 100644 --- a/packages/core/http/core-http-server-internal/src/http_service.ts +++ b/packages/core/http/core-http-server-internal/src/http_service.ts @@ -76,8 +76,8 @@ export class HttpService configService.atPath(externalUrlConfig.path), ]).pipe(map(([http, csp, externalUrl]) => new HttpConfig(http, csp, externalUrl))); const shutdownTimeout$ = this.config$.pipe(map(({ shutdownTimeout }) => shutdownTimeout)); - this.prebootServer = new HttpServer(logger, 'Preboot', shutdownTimeout$); - this.httpServer = new HttpServer(logger, 'Kibana', shutdownTimeout$); + this.prebootServer = new HttpServer(coreContext, 'Preboot', shutdownTimeout$); + this.httpServer = new HttpServer(coreContext, 'Kibana', shutdownTimeout$); this.httpsRedirectServer = new HttpsRedirectServer(logger.get('http', 'redirect', 'server')); } diff --git a/packages/core/http/core-http-server-internal/src/static_assets.test.ts b/packages/core/http/core-http-server-internal/src/static_assets.test.ts deleted file mode 100644 index 0b1acd4e73fd9..0000000000000 --- a/packages/core/http/core-http-server-internal/src/static_assets.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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { StaticAssets } from './static_assets'; -import { BasePath } from './base_path_service'; -import { CdnConfig } from './cdn'; - -describe('StaticAssets', () => { - let basePath: BasePath; - let cdnConfig: CdnConfig; - let staticAssets: StaticAssets; - - beforeEach(() => { - basePath = new BasePath('/base-path'); - }); - - describe('#getHrefBase()', () => { - it('provides fallback to server base path', () => { - cdnConfig = CdnConfig.from(); - staticAssets = new StaticAssets(basePath, cdnConfig); - expect(staticAssets.getHrefBase()).toEqual('/base-path'); - }); - - it('provides the correct HREF given a CDN is configured', () => { - cdnConfig = CdnConfig.from({ url: 'https://cdn.example.com/test' }); - staticAssets = new StaticAssets(basePath, cdnConfig); - expect(staticAssets.getHrefBase()).toEqual('https://cdn.example.com/test'); - }); - }); - - describe('#getPluginAssetHref()', () => { - it('returns the expected value when CDN config is not set', () => { - cdnConfig = CdnConfig.from(); - staticAssets = new StaticAssets(basePath, cdnConfig); - expect(staticAssets.getPluginAssetHref('foo', 'path/to/img.gif')).toEqual( - '/base-path/plugins/foo/assets/path/to/img.gif' - ); - }); - - it('returns the expected value when CDN config is set', () => { - cdnConfig = CdnConfig.from({ url: 'https://cdn.example.com/test' }); - staticAssets = new StaticAssets(basePath, cdnConfig); - expect(staticAssets.getPluginAssetHref('bar', 'path/to/img.gif')).toEqual( - 'https://cdn.example.com/test/plugins/bar/assets/path/to/img.gif' - ); - }); - - it('removes leading slash from the', () => { - cdnConfig = CdnConfig.from(); - staticAssets = new StaticAssets(basePath, cdnConfig); - expect(staticAssets.getPluginAssetHref('dolly', '/path/for/something.svg')).toEqual( - '/base-path/plugins/dolly/assets/path/for/something.svg' - ); - }); - }); -}); diff --git a/packages/core/http/core-http-server-internal/src/static_assets.ts b/packages/core/http/core-http-server-internal/src/static_assets.ts deleted file mode 100644 index 4dfe46d8d31a6..0000000000000 --- a/packages/core/http/core-http-server-internal/src/static_assets.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import type { BasePath } from './base_path_service'; -import { CdnConfig } from './cdn'; - -export interface InternalStaticAssets { - getHrefBase(): string; - getPluginAssetHref(pluginName: string, assetPath: string): string; -} - -export class StaticAssets implements InternalStaticAssets { - private readonly assetsHrefBase: string; - - constructor(basePath: BasePath, cdnConfig: CdnConfig) { - const hrefToUse = cdnConfig.baseHref ?? basePath.serverBasePath; - this.assetsHrefBase = hrefToUse.endsWith('/') ? hrefToUse.slice(0, -1) : hrefToUse; - } - - /** - * Returns a href (hypertext reference) intended to be used as the base for constructing - * other hrefs to static assets. - */ - getHrefBase(): string { - return this.assetsHrefBase; - } - - getPluginAssetHref(pluginName: string, assetPath: string): string { - if (assetPath.startsWith('/')) { - assetPath = assetPath.slice(1); - } - return `${this.assetsHrefBase}/plugins/${pluginName}/assets/${assetPath}`; - } -} diff --git a/packages/core/http/core-http-server-internal/src/static_assets/index.ts b/packages/core/http/core-http-server-internal/src/static_assets/index.ts new file mode 100644 index 0000000000000..1f4dc880583c3 --- /dev/null +++ b/packages/core/http/core-http-server-internal/src/static_assets/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { type InternalStaticAssets, StaticAssets } from './static_assets'; diff --git a/packages/core/http/core-http-server-internal/src/static_assets/static_assets.test.ts b/packages/core/http/core-http-server-internal/src/static_assets/static_assets.test.ts new file mode 100644 index 0000000000000..438a87765d85e --- /dev/null +++ b/packages/core/http/core-http-server-internal/src/static_assets/static_assets.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { StaticAssets, type StaticAssetsParams } from './static_assets'; +import { BasePath } from '../base_path_service'; +import { CdnConfig } from '../cdn_config'; + +describe('StaticAssets', () => { + let basePath: BasePath; + let cdnConfig: CdnConfig; + let staticAssets: StaticAssets; + let args: StaticAssetsParams; + + beforeEach(() => { + basePath = new BasePath('/base-path'); + cdnConfig = CdnConfig.from(); + args = { basePath, cdnConfig, shaDigest: '' }; + }); + + describe('#getHrefBase()', () => { + it('provides fallback to server base path', () => { + staticAssets = new StaticAssets(args); + expect(staticAssets.getHrefBase()).toEqual('/base-path'); + }); + + it('provides the correct HREF given a CDN is configured', () => { + args.cdnConfig = CdnConfig.from({ url: 'https://cdn.example.com/test' }); + staticAssets = new StaticAssets(args); + expect(staticAssets.getHrefBase()).toEqual('https://cdn.example.com/test'); + }); + }); + + describe('#getPluginAssetHref()', () => { + it('returns the expected value when CDN is not configured', () => { + staticAssets = new StaticAssets(args); + expect(staticAssets.getPluginAssetHref('foo', 'path/to/img.gif')).toEqual( + '/base-path/plugins/foo/assets/path/to/img.gif' + ); + }); + + it('returns the expected value when CDN is configured', () => { + args.cdnConfig = CdnConfig.from({ url: 'https://cdn.example.com/test' }); + staticAssets = new StaticAssets(args); + expect(staticAssets.getPluginAssetHref('bar', 'path/to/img.gif')).toEqual( + 'https://cdn.example.com/test/plugins/bar/assets/path/to/img.gif' + ); + }); + + it('removes leading and trailing slash from the assetPath', () => { + staticAssets = new StaticAssets(args); + expect(staticAssets.getPluginAssetHref('dolly', '/path/for/something.svg/')).toEqual( + '/base-path/plugins/dolly/assets/path/for/something.svg' + ); + }); + it('removes leading and trailing slash from the assetPath when CDN is configured', () => { + args.cdnConfig = CdnConfig.from({ url: 'https://cdn.example.com/test' }); + staticAssets = new StaticAssets(args); + expect(staticAssets.getPluginAssetHref('dolly', '/path/for/something.svg/')).toEqual( + 'https://cdn.example.com/test/plugins/dolly/assets/path/for/something.svg' + ); + }); + }); + + describe('with a SHA digest provided', () => { + describe('cdn', () => { + it.each([ + ['https://cdn.example.com', 'https://cdn.example.com/beef', undefined], + ['https://cdn.example.com:1234', 'https://cdn.example.com:1234/beef', undefined], + [ + 'https://cdn.example.com:1234/roast', + 'https://cdn.example.com:1234/roast/beef', + undefined, + ], + // put slashes around shaDigest + [ + 'https://cdn.example.com:1234/roast-slash', + 'https://cdn.example.com:1234/roast-slash/beef', + '/beef/', + ], + ])('suffixes the digest to the CDNs path value (%s)', (url, expectedHref, shaDigest) => { + args.shaDigest = shaDigest ?? 'beef'; + args.cdnConfig = CdnConfig.from({ url }); + staticAssets = new StaticAssets(args); + expect(staticAssets.getHrefBase()).toEqual(expectedHref); + }); + }); + + describe('base path', () => { + it.each([ + ['', '/beef', undefined], + ['/', '/beef', undefined], + ['/roast', '/roast/beef', undefined], + ['/roast/', '/roast/beef', '/beef/'], // cheeky test adding a slashes to digest + ])('suffixes the digest to the server base path "%s")', (url, expectedPath, shaDigest) => { + basePath = new BasePath(url); + args.basePath = basePath; + args.shaDigest = shaDigest ?? 'beef'; + staticAssets = new StaticAssets(args); + expect(staticAssets.getHrefBase()).toEqual(expectedPath); + }); + }); + }); + + describe('#getPluginServerPath()', () => { + it('provides the path plugin assets can use for server routes', () => { + args.shaDigest = '1234'; + staticAssets = new StaticAssets(args); + expect(staticAssets.getPluginServerPath('myPlugin', '/fun/times')).toEqual( + '/1234/plugins/myPlugin/assets/fun/times' + ); + }); + }); + describe('#prependPublicUrl()', () => { + it('with a CDN it appends as expected', () => { + args.cdnConfig = CdnConfig.from({ url: 'http://cdn.example.com/cool?123=true' }); + staticAssets = new StaticAssets(args); + expect(staticAssets.prependPublicUrl('beans')).toEqual( + 'http://cdn.example.com/cool/beans?123=true' + ); + }); + + it('without a CDN it appends as expected', () => { + staticAssets = new StaticAssets(args); + expect(staticAssets.prependPublicUrl('/cool/beans')).toEqual('/base-path/cool/beans'); + }); + }); +}); diff --git a/packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts b/packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts new file mode 100644 index 0000000000000..f5f7d7ac80430 --- /dev/null +++ b/packages/core/http/core-http-server-internal/src/static_assets/static_assets.ts @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { BasePath } from '../base_path_service'; +import { CdnConfig } from '../cdn_config'; +import { + suffixPathnameToPathname, + suffixPathnameToURLPathname, + removeSurroundingSlashes, +} from './util'; + +export interface InternalStaticAssets { + getHrefBase(): string; + /** + * Intended for use by server code rendering UI or generating links to static assets + * that will ultimately be called from the browser and must respect settings like + * serverBasePath + */ + getPluginAssetHref(pluginName: string, assetPath: string): string; + /** + * Intended for use by server code wanting to register static assets against Kibana + * as server paths + */ + getPluginServerPath(pluginName: string, assetPath: string): string; + /** + * Similar to getPluginServerPath, but not plugin-scoped + */ + prependServerPath(pathname: string): string; + + /** + * Will append the given path segment to the configured public path. + * + * @note This could return a path or full URL depending on whether a CDN is configured. + */ + prependPublicUrl(pathname: string): string; +} + +/** @internal */ +export interface StaticAssetsParams { + basePath: BasePath; + cdnConfig: CdnConfig; + shaDigest: string; +} + +/** + * Convention is for trailing slashes in pathnames are stripped. + */ +export class StaticAssets implements InternalStaticAssets { + private readonly assetsHrefBase: string; + private readonly assetsServerPathBase: string; + private readonly hasCdnHost: boolean; + + constructor({ basePath, cdnConfig, shaDigest }: StaticAssetsParams) { + const cdnBaseHref = cdnConfig.baseHref; + if (cdnBaseHref) { + this.hasCdnHost = true; + this.assetsHrefBase = suffixPathnameToURLPathname(cdnBaseHref, shaDigest); + } else { + this.hasCdnHost = false; + this.assetsHrefBase = suffixPathnameToPathname(basePath.serverBasePath, shaDigest); + } + this.assetsServerPathBase = `/${shaDigest}`; + } + + /** + * Returns a href (hypertext reference) intended to be used as the base for constructing + * other hrefs to static assets. + */ + public getHrefBase(): string { + return this.assetsHrefBase; + } + + public getPluginAssetHref(pluginName: string, assetPath: string): string { + if (assetPath.startsWith('/')) { + assetPath = assetPath.slice(1); + } + return `${this.assetsHrefBase}/plugins/${pluginName}/assets/${removeSurroundingSlashes( + assetPath + )}`; + } + + public prependServerPath(path: string): string { + return `${this.assetsServerPathBase}/${removeSurroundingSlashes(path)}`; + } + + public prependPublicUrl(pathname: string): string { + if (this.hasCdnHost) { + return suffixPathnameToURLPathname(this.assetsHrefBase, pathname); + } + return suffixPathnameToPathname(this.assetsHrefBase, pathname); + } + + public getPluginServerPath(pluginName: string, assetPath: string): string { + return `${this.assetsServerPathBase}/plugins/${pluginName}/assets/${removeSurroundingSlashes( + assetPath + )}`; + } +} diff --git a/packages/core/http/core-http-server-internal/src/static_assets/util.ts b/packages/core/http/core-http-server-internal/src/static_assets/util.ts new file mode 100644 index 0000000000000..c86f56fc239a2 --- /dev/null +++ b/packages/core/http/core-http-server-internal/src/static_assets/util.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { URL, format } from 'node:url'; + +function isEmptyPathname(pathname: string): boolean { + return !pathname || pathname === '/'; +} + +function removeTailSlashes(pathname: string): string { + return pathname.replace(/\/+$/, ''); +} + +function removeLeadSlashes(pathname: string): string { + return pathname.replace(/^\/+/, ''); +} + +export function removeSurroundingSlashes(pathname: string): string { + return removeLeadSlashes(removeTailSlashes(pathname)); +} + +export function suffixPathnameToURLPathname(urlString: string, pathname: string): string { + const url = new URL(urlString); + url.pathname = suffixPathnameToPathname(url.pathname, pathname); + return format(url); +} + +/** + * Appends a value to pathname. Pathname is assumed to come from URL.pathname + * Also do some quality control on the path to ensure that it matches URL.pathname. + */ +export function suffixPathnameToPathname(pathnameA: string, pathnameB: string): string { + if (isEmptyPathname(pathnameA)) { + return `/${removeSurroundingSlashes(pathnameB)}`; + } + if (isEmptyPathname(pathnameB)) { + return `/${removeSurroundingSlashes(pathnameA)}`; + } + return `/${removeSurroundingSlashes(pathnameA)}/${removeSurroundingSlashes(pathnameB)}`; +} diff --git a/packages/core/http/core-http-server-internal/tsconfig.json b/packages/core/http/core-http-server-internal/tsconfig.json index e163741c21c7e..7c52ff584a532 100644 --- a/packages/core/http/core-http-server-internal/tsconfig.json +++ b/packages/core/http/core-http-server-internal/tsconfig.json @@ -33,6 +33,7 @@ "@kbn/core-execution-context-server-mocks", "@kbn/core-http-context-server-mocks", "@kbn/logging-mocks", + "@kbn/core-base-server-mocks", ], "exclude": [ "target/**/*", diff --git a/packages/core/http/core-http-server-mocks/src/http_service.mock.ts b/packages/core/http/core-http-server-mocks/src/http_service.mock.ts index f8ecfadfeb87e..7172accf98a9f 100644 --- a/packages/core/http/core-http-server-mocks/src/http_service.mock.ts +++ b/packages/core/http/core-http-server-mocks/src/http_service.mock.ts @@ -87,8 +87,11 @@ const createInternalStaticAssetsMock = ( basePath: BasePathMocked, cdnUrl: undefined | string = undefined ): InternalStaticAssetsMocked => ({ - getHrefBase: jest.fn(() => cdnUrl ?? basePath.serverBasePath), + getHrefBase: jest.fn().mockReturnValue(cdnUrl ?? basePath.serverBasePath), getPluginAssetHref: jest.fn().mockReturnValue(cdnUrl ?? basePath.serverBasePath), + getPluginServerPath: jest.fn((v, _) => v), + prependServerPath: jest.fn((v) => v), + prependPublicUrl: jest.fn((v) => v), }); const createAuthMock = () => { @@ -212,6 +215,7 @@ const createSetupContractMock = < getServerInfo: internalMock.getServerInfo, staticAssets: { getPluginAssetHref: jest.fn().mockImplementation((assetPath: string) => assetPath), + prependPublicUrl: jest.fn().mockImplementation((pathname: string) => pathname), }, }; @@ -227,6 +231,7 @@ const createStartContractMock = () => { getServerInfo: jest.fn(), staticAssets: { getPluginAssetHref: jest.fn().mockImplementation((assetPath: string) => assetPath), + prependPublicUrl: jest.fn().mockImplementation((pathname: string) => pathname), }, }; diff --git a/packages/core/http/core-http-server/src/static_assets.ts b/packages/core/http/core-http-server/src/static_assets.ts index c0cc8597d1540..a839beae8023e 100644 --- a/packages/core/http/core-http-server/src/static_assets.ts +++ b/packages/core/http/core-http-server/src/static_assets.ts @@ -23,4 +23,23 @@ export interface IStaticAssets { * ``` */ getPluginAssetHref(assetPath: string): string; + + /** + * Will return an href, either a path for or full URL with the provided path + * appended to the static assets public base path. + * + * Useful for instances were you need to render your own HTML page and link to + * certain static assets. + * + * @example + * ```ts + * // I want to retrieve the href for Kibana's favicon, requires knowledge of path: + * const favIconHref = core.http.statisAssets.prependPublicUrl('/ui/favicons/favicon.svg'); + * ``` + * + * @note Only use this if you know what you are doing and there is no other option. + * This creates a strong coupling between asset dir structure and your code. + * @param pathname + */ + prependPublicUrl(pathname: string): string; } diff --git a/packages/core/plugins/core-plugins-browser-internal/src/test_helpers/mocks.ts b/packages/core/plugins/core-plugins-browser-internal/src/test_helpers/mocks.ts index 787a7d7678194..38b86ed553cf2 100644 --- a/packages/core/plugins/core-plugins-browser-internal/src/test_helpers/mocks.ts +++ b/packages/core/plugins/core-plugins-browser-internal/src/test_helpers/mocks.ts @@ -23,6 +23,7 @@ export const createPluginInitializerContextMock = (config: unknown = {}) => { branch: 'branch', buildNum: 100, buildSha: 'buildSha', + buildShaShort: 'buildShaShort', dist: false, buildDate: new Date('2023-05-15T23:12:09.000Z'), buildFlavor: 'traditional', diff --git a/packages/core/plugins/core-plugins-browser-mocks/src/plugins_service.mock.ts b/packages/core/plugins/core-plugins-browser-mocks/src/plugins_service.mock.ts index ff3d60c5c5706..6a52194d520f2 100644 --- a/packages/core/plugins/core-plugins-browser-mocks/src/plugins_service.mock.ts +++ b/packages/core/plugins/core-plugins-browser-mocks/src/plugins_service.mock.ts @@ -49,6 +49,7 @@ const createPluginInitializerContextMock = ( branch: 'branch', buildNum: 100, buildSha: 'buildSha', + buildShaShort: 'buildShaShort', dist: false, buildDate: new Date('2023-05-15T23:12:09.000Z'), buildFlavor, diff --git a/packages/core/plugins/core-plugins-server-internal/src/discovery/plugin_manifest_parser.test.ts b/packages/core/plugins/core-plugins-server-internal/src/discovery/plugin_manifest_parser.test.ts index 202cef2ca09d4..cdcf7ac7063f3 100644 --- a/packages/core/plugins/core-plugins-server-internal/src/discovery/plugin_manifest_parser.test.ts +++ b/packages/core/plugins/core-plugins-server-internal/src/discovery/plugin_manifest_parser.test.ts @@ -19,6 +19,7 @@ const packageInfo: PackageInfo = { branch: 'master', buildNum: 1, buildSha: '', + buildShaShort: '', version: '7.0.0-alpha1', dist: false, buildDate: new Date('2023-05-15T23:12:09.000Z'), diff --git a/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts b/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts index c3ed7f6a433b9..3cb8777d647d1 100644 --- a/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts +++ b/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts @@ -236,6 +236,7 @@ export function createPluginSetupContext({ registerOnPreResponse: deps.http.registerOnPreResponse, basePath: deps.http.basePath, staticAssets: { + prependPublicUrl: (pathname: string) => deps.http.staticAssets.prependPublicUrl(pathname), getPluginAssetHref: (assetPath: string) => deps.http.staticAssets.getPluginAssetHref(plugin.name, assetPath), }, @@ -329,6 +330,7 @@ export function createPluginStartContext({ basePath: deps.http.basePath, getServerInfo: deps.http.getServerInfo, staticAssets: { + prependPublicUrl: (pathname: string) => deps.http.staticAssets.prependPublicUrl(pathname), getPluginAssetHref: (assetPath: string) => deps.http.staticAssets.getPluginAssetHref(plugin.name, assetPath), }, diff --git a/packages/core/plugins/core-plugins-server-internal/src/plugins_service.test.ts b/packages/core/plugins/core-plugins-server-internal/src/plugins_service.test.ts index 2185bfe13fb19..e25a1b924e420 100644 --- a/packages/core/plugins/core-plugins-server-internal/src/plugins_service.test.ts +++ b/packages/core/plugins/core-plugins-server-internal/src/plugins_service.test.ts @@ -1165,8 +1165,10 @@ describe('PluginsService', () => { }); describe('plugin initialization', () => { + let prebootPlugins: PluginWrapper[]; + let standardPlugins: PluginWrapper[]; beforeEach(() => { - const prebootPlugins = [ + prebootPlugins = [ createPlugin('plugin-1-preboot', { type: PluginType.preboot, path: 'path-1-preboot', @@ -1178,7 +1180,7 @@ describe('PluginsService', () => { version: 'version-2', }), ]; - const standardPlugins = [ + standardPlugins = [ createPlugin('plugin-1-standard', { path: 'path-1-standard', version: 'version-1', @@ -1299,6 +1301,31 @@ describe('PluginsService', () => { expect(standardMockPluginSystem.setupPlugins).not.toHaveBeenCalled(); }); + it('#preboot registers expected static dirs', async () => { + prebootDeps.http.staticAssets.getPluginServerPath.mockImplementation( + (pluginName: string) => `/static-assets/${pluginName}` + ); + await pluginsService.discover({ environment: environmentPreboot, node: nodePreboot }); + await pluginsService.preboot(prebootDeps); + expect(prebootDeps.http.registerStaticDir).toHaveBeenCalledTimes(prebootPlugins.length * 2); + expect(prebootDeps.http.registerStaticDir).toHaveBeenCalledWith( + '/static-assets/plugin-1-preboot', + expect.any(String) + ); + expect(prebootDeps.http.registerStaticDir).toHaveBeenCalledWith( + '/plugins/plugin-1-preboot/assets/{path*}', + expect.any(String) + ); + expect(prebootDeps.http.registerStaticDir).toHaveBeenCalledWith( + '/static-assets/plugin-2-preboot', + expect.any(String) + ); + expect(prebootDeps.http.registerStaticDir).toHaveBeenCalledWith( + '/plugins/plugin-2-preboot/assets/{path*}', + expect.any(String) + ); + }); + it('#setup does initialize `standard` plugins if plugins.initialize is true', async () => { config$.next({ plugins: { initialize: true } }); await pluginsService.discover({ environment: environmentPreboot, node: nodePreboot }); @@ -1319,6 +1346,32 @@ describe('PluginsService', () => { expect(prebootMockPluginSystem.setupPlugins).not.toHaveBeenCalled(); expect(initialized).toBe(false); }); + + it('#setup registers expected static dirs', async () => { + await pluginsService.discover({ environment: environmentPreboot, node: nodePreboot }); + await pluginsService.preboot(prebootDeps); + setupDeps.http.staticAssets.getPluginServerPath.mockImplementation( + (pluginName: string) => `/static-assets/${pluginName}` + ); + await pluginsService.setup(setupDeps); + expect(setupDeps.http.registerStaticDir).toHaveBeenCalledTimes(standardPlugins.length * 2); + expect(setupDeps.http.registerStaticDir).toHaveBeenCalledWith( + '/static-assets/plugin-1-standard', + expect.any(String) + ); + expect(setupDeps.http.registerStaticDir).toHaveBeenCalledWith( + '/plugins/plugin-1-standard/assets/{path*}', + expect.any(String) + ); + expect(setupDeps.http.registerStaticDir).toHaveBeenCalledWith( + '/static-assets/plugin-2-standard', + expect.any(String) + ); + expect(setupDeps.http.registerStaticDir).toHaveBeenCalledWith( + '/plugins/plugin-2-standard/assets/{path*}', + expect.any(String) + ); + }); }); describe('#getExposedPluginConfigsToUsage', () => { diff --git a/packages/core/plugins/core-plugins-server-internal/src/plugins_service.ts b/packages/core/plugins/core-plugins-server-internal/src/plugins_service.ts index a5f7bfaef7d73..da5d77d8be675 100644 --- a/packages/core/plugins/core-plugins-server-internal/src/plugins_service.ts +++ b/packages/core/plugins/core-plugins-server-internal/src/plugins_service.ts @@ -448,10 +448,16 @@ export class PluginsService uiPluginInternalInfo: Map ) { for (const [pluginName, pluginInfo] of uiPluginInternalInfo) { - deps.http.registerStaticDir( + /** + * Serve UI from sha-scoped and not-sha-scoped paths to allow time for plugin code to migrate + * Eventually we only want to serve from the sha scoped path + */ + [ + deps.http.staticAssets.getPluginServerPath(pluginName, '{path*}'), `/plugins/${pluginName}/assets/{path*}`, - pluginInfo.publicAssetsDir - ); + ].forEach((path) => { + deps.http.registerStaticDir(path, pluginInfo.publicAssetsDir); + }); } } } diff --git a/packages/core/rendering/core-rendering-server-internal/src/__snapshots__/rendering_service.test.ts.snap b/packages/core/rendering/core-rendering-server-internal/src/__snapshots__/rendering_service.test.ts.snap index 535624e4a8320..b6fedfd8644e4 100644 --- a/packages/core/rendering/core-rendering-server-internal/src/__snapshots__/rendering_service.test.ts.snap +++ b/packages/core/rendering/core-rendering-server-internal/src/__snapshots__/rendering_service.test.ts.snap @@ -24,6 +24,7 @@ Object { "buildFlavor": Any, "buildNum": Any, "buildSha": Any, + "buildShaShort": "XXXXXX", "dist": Any, "version": Any, }, @@ -92,6 +93,7 @@ Object { "buildFlavor": Any, "buildNum": Any, "buildSha": Any, + "buildShaShort": "XXXXXX", "dist": Any, "version": Any, }, @@ -156,6 +158,7 @@ Object { "buildFlavor": Any, "buildNum": Any, "buildSha": Any, + "buildShaShort": "XXXXXX", "dist": Any, "version": Any, }, @@ -224,6 +227,7 @@ Object { "buildFlavor": Any, "buildNum": Any, "buildSha": Any, + "buildShaShort": "XXXXXX", "dist": Any, "version": Any, }, @@ -288,6 +292,7 @@ Object { "buildFlavor": Any, "buildNum": Any, "buildSha": Any, + "buildShaShort": "XXXXXX", "dist": Any, "version": Any, }, @@ -352,6 +357,7 @@ Object { "buildFlavor": Any, "buildNum": Any, "buildSha": Any, + "buildShaShort": "XXXXXX", "dist": Any, "version": Any, }, @@ -420,6 +426,7 @@ Object { "buildFlavor": Any, "buildNum": Any, "buildSha": Any, + "buildShaShort": "XXXXXX", "dist": Any, "version": Any, }, @@ -484,6 +491,7 @@ Object { "buildFlavor": Any, "buildNum": Any, "buildSha": Any, + "buildShaShort": "XXXXXX", "dist": Any, "version": Any, }, @@ -553,6 +561,7 @@ Object { "buildFlavor": Any, "buildNum": Any, "buildSha": Any, + "buildShaShort": "XXXXXX", "dist": Any, "version": Any, }, @@ -621,6 +630,7 @@ Object { "buildFlavor": Any, "buildNum": Any, "buildSha": Any, + "buildShaShort": "XXXXXX", "dist": Any, "version": Any, }, @@ -690,6 +700,7 @@ Object { "buildFlavor": Any, "buildNum": Any, "buildSha": Any, + "buildShaShort": "XXXXXX", "dist": Any, "version": Any, }, @@ -763,6 +774,7 @@ Object { "buildFlavor": Any, "buildNum": Any, "buildSha": Any, + "buildShaShort": "XXXXXX", "dist": Any, "version": Any, }, @@ -827,6 +839,7 @@ Object { "buildFlavor": Any, "buildNum": Any, "buildSha": Any, + "buildShaShort": "XXXXXX", "dist": Any, "version": Any, }, @@ -896,6 +909,7 @@ Object { "buildFlavor": Any, "buildNum": Any, "buildSha": Any, + "buildShaShort": "XXXXXX", "dist": Any, "version": Any, }, @@ -969,6 +983,7 @@ Object { "buildFlavor": Any, "buildNum": Any, "buildSha": Any, + "buildShaShort": "XXXXXX", "dist": Any, "version": Any, }, @@ -1038,6 +1053,7 @@ Object { "buildFlavor": Any, "buildNum": Any, "buildSha": Any, + "buildShaShort": "XXXXXX", "dist": Any, "version": Any, }, diff --git a/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.test.ts b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.test.ts index 5c699e905e9cd..e959b3aff356d 100644 --- a/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.test.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.test.ts @@ -25,6 +25,7 @@ const createPackageInfo = (parts: Partial = {}): PackageInfo => ({ branch: 'master', buildNum: 42, buildSha: 'buildSha', + buildShaShort: 'buildShaShort', buildDate: new Date('2023-05-15T23:12:09.000Z'), dist: false, version: '8.0.0', @@ -62,7 +63,7 @@ describe('bootstrapRenderer', () => { auth, packageInfo, uiPlugins, - baseHref: '/base-path', + baseHref: `/base-path/${packageInfo.buildShaShort}`, // the base href as provided by static assets module }); }); @@ -319,7 +320,7 @@ describe('bootstrapRenderer', () => { expect(getPluginsBundlePathsMock).toHaveBeenCalledWith({ isAnonymousPage, uiPlugins, - bundlesHref: '/base-path/42/bundles', + bundlesHref: '/base-path/buildShaShort/bundles', }); }); }); @@ -338,7 +339,7 @@ describe('bootstrapRenderer', () => { expect(getJsDependencyPathsMock).toHaveBeenCalledTimes(1); expect(getJsDependencyPathsMock).toHaveBeenCalledWith( - '/base-path/42/bundles', + '/base-path/buildShaShort/bundles', pluginsBundlePaths ); }); diff --git a/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.ts b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.ts index e8c30819a0b6e..57cd247b4f6f5 100644 --- a/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.ts @@ -79,8 +79,7 @@ export const bootstrapRendererFactory: BootstrapRendererFactory = ({ themeVersion, darkMode, }); - const buildHash = packageInfo.buildNum; - const bundlesHref = getBundlesHref(baseHref, String(buildHash)); + const bundlesHref = getBundlesHref(baseHref); const bundlePaths = getPluginsBundlePaths({ uiPlugins, diff --git a/packages/core/rendering/core-rendering-server-internal/src/render_utils.test.ts b/packages/core/rendering/core-rendering-server-internal/src/render_utils.test.ts index 8a5d2e4c7377b..e52e18e03776b 100644 --- a/packages/core/rendering/core-rendering-server-internal/src/render_utils.test.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/render_utils.test.ts @@ -16,14 +16,14 @@ describe('getStylesheetPaths', () => { getStylesheetPaths({ darkMode: true, themeVersion: 'v8', - baseHref: '/base-path', + baseHref: '/base-path/buildShaShort', buildNum: 17, }) ).toMatchInlineSnapshot(` Array [ - "/base-path/17/bundles/kbn-ui-shared-deps-npm/kbn-ui-shared-deps-npm.v8.dark.css", - "/base-path/17/bundles/kbn-ui-shared-deps-src/kbn-ui-shared-deps-src.css", - "/base-path/ui/legacy_dark_theme.min.css", + "/base-path/buildShaShort/bundles/kbn-ui-shared-deps-npm/kbn-ui-shared-deps-npm.v8.dark.css", + "/base-path/buildShaShort/bundles/kbn-ui-shared-deps-src/kbn-ui-shared-deps-src.css", + "/base-path/buildShaShort/ui/legacy_dark_theme.min.css", ] `); }); @@ -36,14 +36,14 @@ describe('getStylesheetPaths', () => { getStylesheetPaths({ darkMode: false, themeVersion: 'v8', - baseHref: '/base-path', + baseHref: '/base-path/buildShaShort', buildNum: 69, }) ).toMatchInlineSnapshot(` Array [ - "/base-path/69/bundles/kbn-ui-shared-deps-npm/kbn-ui-shared-deps-npm.v8.light.css", - "/base-path/69/bundles/kbn-ui-shared-deps-src/kbn-ui-shared-deps-src.css", - "/base-path/ui/legacy_light_theme.min.css", + "/base-path/buildShaShort/bundles/kbn-ui-shared-deps-npm/kbn-ui-shared-deps-npm.v8.light.css", + "/base-path/buildShaShort/bundles/kbn-ui-shared-deps-src/kbn-ui-shared-deps-src.css", + "/base-path/buildShaShort/ui/legacy_light_theme.min.css", ] `); }); diff --git a/packages/core/rendering/core-rendering-server-internal/src/render_utils.ts b/packages/core/rendering/core-rendering-server-internal/src/render_utils.ts index 6f74320098b16..51f15a2ba034d 100644 --- a/packages/core/rendering/core-rendering-server-internal/src/render_utils.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/render_utils.ts @@ -22,8 +22,7 @@ export const getSettingValue = ( return convert(value); }; -export const getBundlesHref = (baseHref: string, buildNr: string): string => - `${baseHref}/${buildNr}/bundles`; +export const getBundlesHref = (baseHref: string): string => `${baseHref}/bundles`; export const getStylesheetPaths = ({ themeVersion, @@ -36,7 +35,7 @@ export const getStylesheetPaths = ({ buildNum: number; baseHref: string; }) => { - const bundlesHref = getBundlesHref(baseHref, String(buildNum)); + const bundlesHref = getBundlesHref(baseHref); return [ ...(darkMode ? [ diff --git a/packages/kbn-coloring/src/palettes/constants.ts b/packages/kbn-coloring/src/palettes/constants.ts index ee9bb9b02b29b..fb35d51c0db13 100644 --- a/packages/kbn-coloring/src/palettes/constants.ts +++ b/packages/kbn-coloring/src/palettes/constants.ts @@ -14,3 +14,7 @@ export const DEFAULT_RANGE_TYPE = 'percent'; export const DEFAULT_MIN_STOP = 0; export const DEFAULT_MAX_STOP = 100; export const DEFAULT_COLOR_STEPS = 5; + +export const DEFAULT_FALLBACK_PALETTE = 'default'; +export const LEGACY_COMPLIMENTARY_PALETTE = 'complimentary'; +export const COMPLEMENTARY_PALETTE = 'complementary'; diff --git a/packages/kbn-coloring/src/palettes/utils.ts b/packages/kbn-coloring/src/palettes/utils.ts index d3a51388c7a3d..406da5fb7c998 100644 --- a/packages/kbn-coloring/src/palettes/utils.ts +++ b/packages/kbn-coloring/src/palettes/utils.ts @@ -19,6 +19,9 @@ import { DEFAULT_PALETTE_NAME, DEFAULT_MAX_STOP, DEFAULT_MIN_STOP, + DEFAULT_FALLBACK_PALETTE, + LEGACY_COMPLIMENTARY_PALETTE, + COMPLEMENTARY_PALETTE, } from './constants'; /** @internal **/ @@ -189,3 +192,14 @@ export function reversePalette(paletteColorRepresentation: ColorStop[] = []) { })) .reverse(); } + +// This is a helper function used for backwards compatibility +// for the mispelled complementary palette. +// https://github.com/elastic/kibana/issues/161194 +export function getActivePaletteName(name?: string): string { + let paletteName = name || DEFAULT_FALLBACK_PALETTE; + if (paletteName === LEGACY_COMPLIMENTARY_PALETTE) { + paletteName = COMPLEMENTARY_PALETTE; + } + return paletteName; +} diff --git a/packages/kbn-config/src/__snapshots__/env.test.ts.snap b/packages/kbn-config/src/__snapshots__/env.test.ts.snap index e5d5a3816ced3..4bc87fb5f240e 100644 --- a/packages/kbn-config/src/__snapshots__/env.test.ts.snap +++ b/packages/kbn-config/src/__snapshots__/env.test.ts.snap @@ -32,6 +32,7 @@ Env { "buildFlavor": "traditional", "buildNum": 9007199254740991, "buildSha": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "buildShaShort": "XXXXXXXXXXXX", "dist": false, "version": "v1", }, @@ -75,6 +76,7 @@ Env { "buildFlavor": "traditional", "buildNum": 9007199254740991, "buildSha": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "buildShaShort": "XXXXXXXXXXXX", "dist": false, "version": "v1", }, @@ -117,6 +119,7 @@ Env { "buildFlavor": "traditional", "buildNum": 9007199254740991, "buildSha": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "buildShaShort": "XXXXXXXXXXXX", "dist": false, "version": "some-version", }, @@ -159,6 +162,7 @@ Env { "buildFlavor": "traditional", "buildNum": 100, "buildSha": "feature-v1-build-sha", + "buildShaShort": "feature-v1-b", "dist": true, "version": "v1", }, @@ -201,6 +205,7 @@ Env { "buildFlavor": "traditional", "buildNum": 9007199254740991, "buildSha": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "buildShaShort": "XXXXXXXXXXXX", "dist": false, "version": "v1", }, @@ -243,6 +248,7 @@ Env { "buildFlavor": "traditional", "buildNum": 100, "buildSha": "feature-v1-build-sha", + "buildShaShort": "feature-v1-b", "dist": true, "version": "v1", }, diff --git a/packages/kbn-config/src/env.test.ts b/packages/kbn-config/src/env.test.ts index 7c301ff83e6f4..45f037500b77e 100644 --- a/packages/kbn-config/src/env.test.ts +++ b/packages/kbn-config/src/env.test.ts @@ -248,3 +248,35 @@ describe('packageInfo.buildFlavor', () => { expect(env.packageInfo.buildFlavor).toEqual('traditional'); }); }); + +describe('packageInfo.buildShaShort', () => { + const sha = 'c6e1a25bea71a623929a8f172c0273bf0c811ca0'; + it('provides the sha and a short version of the sha', () => { + mockPackage.raw = { + branch: 'some-branch', + version: 'some-version', + }; + + const env = new Env( + '/some/home/dir', + { + branch: 'whathaveyou', + version: 'v1', + build: { + distributable: true, + number: 100, + sha, + date: BUILD_DATE, + }, + }, + getEnvOptions({ + cliArgs: { dev: false }, + configs: ['/some/other/path/some-kibana.yml'], + repoPackages: ['FakePackage1', 'FakePackage2'] as unknown as Package[], + }) + ); + + expect(env.packageInfo.buildSha).toEqual('c6e1a25bea71a623929a8f172c0273bf0c811ca0'); + expect(env.packageInfo.buildShaShort).toEqual('c6e1a25bea71'); + }); +}); diff --git a/packages/kbn-config/src/env.ts b/packages/kbn-config/src/env.ts index 99728f0dfc413..4b2c936116159 100644 --- a/packages/kbn-config/src/env.ts +++ b/packages/kbn-config/src/env.ts @@ -121,6 +121,7 @@ export class Env { branch: pkg.branch, buildNum: isKibanaDistributable ? pkg.build.number : Number.MAX_SAFE_INTEGER, buildSha: isKibanaDistributable ? pkg.build.sha : 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', + buildShaShort: isKibanaDistributable ? pkg.build.sha.slice(0, 12) : 'XXXXXXXXXXXX', version: pkg.version, dist: isKibanaDistributable, buildDate: isKibanaDistributable ? new Date(pkg.build.date) : new Date(), diff --git a/packages/kbn-config/src/types.ts b/packages/kbn-config/src/types.ts index f9038a1a7fd26..91706bb9f2cb8 100644 --- a/packages/kbn-config/src/types.ts +++ b/packages/kbn-config/src/types.ts @@ -14,6 +14,7 @@ export interface PackageInfo { branch: string; buildNum: number; buildSha: string; + buildShaShort: string; buildDate: Date; buildFlavor: BuildFlavor; dist: boolean; diff --git a/packages/kbn-data-view-utils/README.md b/packages/kbn-data-view-utils/README.md new file mode 100644 index 0000000000000..5912eae9f2b82 --- /dev/null +++ b/packages/kbn-data-view-utils/README.md @@ -0,0 +1,3 @@ +# @kbn/data-view-utils + +Data View utilities. \ No newline at end of file diff --git a/packages/kbn-data-view-utils/index.ts b/packages/kbn-data-view-utils/index.ts new file mode 100644 index 0000000000000..c78869a471cb0 --- /dev/null +++ b/packages/kbn-data-view-utils/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './src/constants'; diff --git a/packages/kbn-data-view-utils/jest.config.js b/packages/kbn-data-view-utils/jest.config.js new file mode 100644 index 0000000000000..3c0a0a118baaf --- /dev/null +++ b/packages/kbn-data-view-utils/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-data-view-utils'], +}; diff --git a/packages/kbn-data-view-utils/kibana.jsonc b/packages/kbn-data-view-utils/kibana.jsonc new file mode 100644 index 0000000000000..a5bd7b958e272 --- /dev/null +++ b/packages/kbn-data-view-utils/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/data-view-utils", + "owner": "@elastic/kibana-data-discovery" +} diff --git a/packages/kbn-data-view-utils/package.json b/packages/kbn-data-view-utils/package.json new file mode 100644 index 0000000000000..1bcf593b4c438 --- /dev/null +++ b/packages/kbn-data-view-utils/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/data-view-utils", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": false +} \ No newline at end of file diff --git a/packages/kbn-data-view-utils/src/constants.ts b/packages/kbn-data-view-utils/src/constants.ts new file mode 100644 index 0000000000000..81759978ef93c --- /dev/null +++ b/packages/kbn-data-view-utils/src/constants.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** + * ESQL type constant + */ + +export const ESQL_TYPE = 'esql'; diff --git a/packages/kbn-data-view-utils/tsconfig.json b/packages/kbn-data-view-utils/tsconfig.json new file mode 100644 index 0000000000000..a41af0b7c5017 --- /dev/null +++ b/packages/kbn-data-view-utils/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "target/**/*" + ] +} diff --git a/packages/kbn-es-types/src/search.ts b/packages/kbn-es-types/src/search.ts index 461a32f149842..4351ac91e5c17 100644 --- a/packages/kbn-es-types/src/search.ts +++ b/packages/kbn-es-types/src/search.ts @@ -678,4 +678,5 @@ export interface ESQLSearchParams { query: string; filter?: unknown; locale?: string; + dropNullColumns?: boolean; } diff --git a/packages/kbn-esql-utils/src/utils/get_esql_adhoc_dataview.ts b/packages/kbn-esql-utils/src/utils/get_esql_adhoc_dataview.ts index a1866526742cc..e1e599946ef59 100644 --- a/packages/kbn-esql-utils/src/utils/get_esql_adhoc_dataview.ts +++ b/packages/kbn-esql-utils/src/utils/get_esql_adhoc_dataview.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; +import { ESQL_TYPE } from '@kbn/data-view-utils'; // uses browser sha256 method with fallback if unavailable async function sha256(str: string) { @@ -32,6 +33,7 @@ export async function getESQLAdHocDataview( ) { return await dataViewsService.create({ title: indexPattern, + type: ESQL_TYPE, id: await sha256(`esql-${indexPattern}`), }); } diff --git a/packages/kbn-esql-utils/src/utils/query_parsing_helpers.test.ts b/packages/kbn-esql-utils/src/utils/query_parsing_helpers.test.ts index 8390e059b4db8..7820d6d9cf7d1 100644 --- a/packages/kbn-esql-utils/src/utils/query_parsing_helpers.test.ts +++ b/packages/kbn-esql-utils/src/utils/query_parsing_helpers.test.ts @@ -43,6 +43,11 @@ describe('sql/esql query helpers', () => { 'SELECT * FROM (SELECT woof, miaou FROM "logstash-1234!" GROUP BY woof)' ); expect(idxPattern8).toBe('logstash-1234!'); + + const idxPattern9 = getIndexPatternFromSQLQuery( + 'SELECT * FROM remote_cluster:logs-* WHERE field > 20' + ); + expect(idxPattern9).toBe('remote_cluster:logs-*'); }); }); @@ -71,6 +76,9 @@ describe('sql/esql query helpers', () => { const idxPattern9 = getIndexPatternFromESQLQuery('FROM foo-1, foo-2 [metadata _id]'); expect(idxPattern9).toBe('foo-1, foo-2'); + + const idxPattern10 = getIndexPatternFromESQLQuery('FROM foo-1, remote_cluster:foo-2, foo-3'); + expect(idxPattern10).toBe('foo-1, remote_cluster:foo-2, foo-3'); }); }); diff --git a/packages/kbn-esql-utils/src/utils/query_parsing_helpers.ts b/packages/kbn-esql-utils/src/utils/query_parsing_helpers.ts index 93e7ddbb6ffe9..6f96a0ae445e8 100644 --- a/packages/kbn-esql-utils/src/utils/query_parsing_helpers.ts +++ b/packages/kbn-esql-utils/src/utils/query_parsing_helpers.ts @@ -17,7 +17,7 @@ export function getIndexPatternFromSQLQuery(sqlQuery?: string): string { sql = `${splitFroms[fromsLength - 2]} FROM ${splitFroms[fromsLength - 1]}`; } // case insensitive match for the index pattern - const regex = new RegExp(/FROM\s+([\w*-.!@$^()~;]+)/, 'i'); + const regex = new RegExp(/FROM\s+([(\w*:)?\w*-.!@$^()~;]+)/, 'i'); const matches = sql?.match(regex); if (matches) { return matches[1]; @@ -34,7 +34,7 @@ export function getIndexPatternFromESQLQuery(esql?: string): string { } const parsedString = esql?.replaceAll('`', ''); // case insensitive match for the index pattern - const regex = new RegExp(/FROM\s+([\w*-.!@$^()~;\s]+)/, 'i'); + const regex = new RegExp(/FROM\s+([(\w*:)?\w*-.!@$^()~;\s]+)/, 'i'); const matches = parsedString?.match(regex); if (matches) { return matches[1]?.trim(); diff --git a/packages/kbn-esql-utils/tsconfig.json b/packages/kbn-esql-utils/tsconfig.json index 2fe775fb7d586..b604fa84c1de3 100644 --- a/packages/kbn-esql-utils/tsconfig.json +++ b/packages/kbn-esql-utils/tsconfig.json @@ -18,5 +18,6 @@ "kbn_references": [ "@kbn/data-views-plugin", "@kbn/crypto-browser", + "@kbn/data-view-utils", ] } diff --git a/packages/kbn-generate-csv/src/__snapshots__/generate_csv.test.ts.snap b/packages/kbn-generate-csv/src/__snapshots__/generate_csv.test.ts.snap index 778ec83563ca7..ec692fe0fea60 100644 --- a/packages/kbn-generate-csv/src/__snapshots__/generate_csv.test.ts.snap +++ b/packages/kbn-generate-csv/src/__snapshots__/generate_csv.test.ts.snap @@ -226,6 +226,14 @@ exports[`CsvGenerator Scroll strategy uses the scroll context to page all the da " `; +exports[`CsvGenerator export behavior when scroll duration config is auto csv gets generated if search resolves without errors before the computed timeout value passed to the search data client elapses 1`] = ` +"a,b +a1,b1 +a1,b1 +a1,b1 +" +`; + exports[`CsvGenerator fields from job.columns (7.13+ generated) cells can be multi-value 1`] = ` "product,category coconut,\\"cool, rad\\" diff --git a/packages/kbn-generate-csv/src/generate_csv.test.ts b/packages/kbn-generate-csv/src/generate_csv.test.ts index 8d831895173ed..86a88fe3d3fb0 100644 --- a/packages/kbn-generate-csv/src/generate_csv.test.ts +++ b/packages/kbn-generate-csv/src/generate_csv.test.ts @@ -9,6 +9,7 @@ import { identity, range } from 'lodash'; import * as Rx from 'rxjs'; import type { Writable } from 'stream'; +import { add, type Duration } from 'date-fns'; import { errors as esErrors, estypes } from '@elastic/elasticsearch'; import type { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; @@ -21,6 +22,8 @@ import { } from '@kbn/core/server/mocks'; import { ISearchClient, ISearchStartSearchSource } from '@kbn/data-plugin/common'; import { searchSourceInstanceMock } from '@kbn/data-plugin/common/search/search_source/mocks'; +import type { IScopedSearchClient } from '@kbn/data-plugin/server'; +import type { IKibanaSearchResponse } from '@kbn/data-plugin/common'; import { dataPluginMock } from '@kbn/data-plugin/server/mocks'; import { FieldFormatsRegistry } from '@kbn/field-formats-plugin/common'; import { CancellationToken } from '@kbn/reporting-common'; @@ -360,7 +363,11 @@ describe('CsvGenerator', () => { expect(mockDataClient.search).toHaveBeenCalledTimes(10); expect(mockDataClient.search).toBeCalledWith( { params: { body: {}, ignore_throttled: undefined, max_concurrent_shard_requests: 5 } }, - { strategy: 'es', transport: { maxRetries: 0, requestTimeout: '30s' } } + { + abortSignal: expect.any(AbortSignal), + strategy: 'es', + transport: { maxRetries: 0, requestTimeout: '30s' }, + } ); expect(mockEsClient.asCurrentUser.openPointInTime).toHaveBeenCalledTimes(1); @@ -370,7 +377,12 @@ describe('CsvGenerator', () => { index: 'logstash-*', keep_alive: '30s', }, - { maxConcurrentShardRequests: 5, maxRetries: 0, requestTimeout: '30s' } + { + maxConcurrentShardRequests: 5, + maxRetries: 0, + requestTimeout: '30s', + signal: expect.any(AbortSignal), + } ); expect(mockEsClient.asCurrentUser.closePointInTime).toHaveBeenCalledTimes(1); @@ -548,6 +560,203 @@ describe('CsvGenerator', () => { }); }); + describe('export behavior when scroll duration config is auto', () => { + const getTaskInstanceFields = (intervalFromNow: Duration) => { + const now = new Date(Date.now()); + return { startedAt: now, retryAt: add(now, intervalFromNow) }; + }; + + let mockConfigWithAutoScrollDuration: ReportingConfigType['csv']; + let mockDataClientSearchFn: jest.MockedFunction; + + beforeEach(() => { + mockConfigWithAutoScrollDuration = { + ...mockConfig, + scroll: { + ...mockConfig.scroll, + duration: 'auto', + }, + }; + + mockDataClientSearchFn = jest.fn(); + + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.clearAllTimers(); + jest.useRealTimers(); + + mockDataClientSearchFn.mockRestore(); + }); + + it('csv gets generated if search resolves without errors before the computed timeout value passed to the search data client elapses', async () => { + const timeFromNowInMs = 4 * 60 * 1000; + + const taskInstanceFields = getTaskInstanceFields({ + seconds: timeFromNowInMs / 1000, + }); + + mockDataClientSearchFn.mockImplementation((_, options) => { + const getSearchResult = () => { + const queuedAt = Date.now(); + + return new Promise>>( + (resolve, reject) => { + setTimeout(() => { + if ( + new Date(Date.now()).getTime() - new Date(queuedAt).getTime() > + Number((options?.transport?.requestTimeout! as string).replace(/ms/, '')) + ) { + reject( + new esErrors.ResponseError({ statusCode: 408, meta: {} as any, warnings: [] }) + ); + } else { + resolve({ + rawResponse: getMockRawResponse( + [ + { + fields: { a: ['a1'], b: ['b1'] }, + } as unknown as estypes.SearchHit, + ], + 3 + ), + }); + } + }, timeFromNowInMs / 4); + } + ); + }; + + return Rx.defer(getSearchResult); + }); + + const generateCsvPromise = new CsvGenerator( + createMockJob({ searchSource: {}, columns: ['a', 'b'] }), + mockConfigWithAutoScrollDuration, + taskInstanceFields, + { + es: mockEsClient, + data: { + ...mockDataClient, + search: mockDataClientSearchFn, + }, + uiSettings: uiSettingsClient, + }, + { + searchSourceStart: mockSearchSourceService, + fieldFormatsRegistry: mockFieldFormatsRegistry, + }, + new CancellationToken(), + mockLogger, + stream + ).generateData(); + + await jest.advanceTimersByTimeAsync(timeFromNowInMs); + + expect(await generateCsvPromise).toEqual( + expect.objectContaining({ + warnings: [], + }) + ); + + expect(mockDataClientSearchFn).toBeCalledWith( + { params: { body: {}, ignore_throttled: undefined, max_concurrent_shard_requests: 5 } }, + { + abortSignal: expect.any(AbortSignal), + strategy: 'es', + transport: { maxRetries: 0, requestTimeout: `${timeFromNowInMs}ms` }, + } + ); + + expect(content).toMatchSnapshot(); + }); + + it('csv generation errors if search request does not resolve before the computed timeout value passed to the search data client elapses', async () => { + const timeFromNowInMs = 4 * 60 * 1000; + + const taskInstanceFields = getTaskInstanceFields({ + seconds: timeFromNowInMs / 1000, + }); + + const requestDuration = timeFromNowInMs + 1000; + + mockDataClientSearchFn.mockImplementation((_, options) => { + const getSearchResult = () => { + const queuedAt = Date.now(); + + return new Promise>>( + (resolve, reject) => { + setTimeout(() => { + if ( + new Date(Date.now()).getTime() - new Date(queuedAt).getTime() > + Number((options?.transport?.requestTimeout! as string).replace(/ms/, '')) + ) { + reject( + new esErrors.ResponseError({ statusCode: 408, meta: {} as any, warnings: [] }) + ); + } else { + resolve({ + rawResponse: getMockRawResponse( + [ + { + fields: { a: ['a1'], b: ['b1'] }, + } as unknown as estypes.SearchHit, + ], + 3 + ), + }); + } + }, requestDuration); + } + ); + }; + + return Rx.defer(getSearchResult); + }); + + const generateCsvPromise = new CsvGenerator( + createMockJob({ searchSource: {}, columns: ['a', 'b'] }), + mockConfigWithAutoScrollDuration, + taskInstanceFields, + { + es: mockEsClient, + data: { + ...mockDataClient, + search: mockDataClientSearchFn, + }, + uiSettings: uiSettingsClient, + }, + { + searchSourceStart: mockSearchSourceService, + fieldFormatsRegistry: mockFieldFormatsRegistry, + }, + new CancellationToken(), + mockLogger, + stream + ).generateData(); + + await jest.advanceTimersByTimeAsync(requestDuration); + + expect(await generateCsvPromise).toEqual( + expect.objectContaining({ + warnings: expect.arrayContaining([ + expect.stringContaining('Received a 408 response from Elasticsearch'), + ]), + }) + ); + + expect(mockDataClientSearchFn).toBeCalledWith( + { params: { body: {}, ignore_throttled: undefined, max_concurrent_shard_requests: 5 } }, + { + abortSignal: expect.any(AbortSignal), + strategy: 'es', + transport: { maxRetries: 0, requestTimeout: `${timeFromNowInMs}ms` }, + } + ); + }); + }); + describe('Scroll strategy', () => { const mockJobUsingScrollPaging = createMockJob({ columns: ['date', 'ip', 'message'], @@ -654,7 +863,11 @@ describe('CsvGenerator', () => { max_concurrent_shard_requests: 5, }), }, - { strategy: 'es', transport: { maxRetries: 0, requestTimeout: '30s' } } + { + abortSignal: expect.any(AbortSignal), + strategy: 'es', + transport: { maxRetries: 0, requestTimeout: '30s' }, + } ); expect(mockEsClient.asCurrentUser.openPointInTime).not.toHaveBeenCalled(); @@ -1200,17 +1413,12 @@ describe('CsvGenerator', () => { index: 'logstash-*', keep_alive: '30s', }, - { maxConcurrentShardRequests: 5, maxRetries: 0, requestTimeout: '30s' } - ); - - expect(mockEsClient.asCurrentUser.openPointInTime).toHaveBeenCalledWith( { - ignore_unavailable: true, - ignore_throttled: false, - index: 'logstash-*', - keep_alive: '30s', - }, - { maxConcurrentShardRequests: 5, maxRetries: 0, requestTimeout: '30s' } + maxConcurrentShardRequests: 5, + maxRetries: 0, + requestTimeout: '30s', + signal: expect.any(AbortSignal), + } ); expect(mockDataClient.search).toBeCalledWith( @@ -1220,7 +1428,11 @@ describe('CsvGenerator', () => { max_concurrent_shard_requests: 5, }, }, - { strategy: 'es', transport: { maxRetries: 0, requestTimeout: '30s' } } + { + abortSignal: expect.any(AbortSignal), + strategy: 'es', + transport: { maxRetries: 0, requestTimeout: '30s' }, + } ); }); diff --git a/packages/kbn-generate-csv/src/generate_csv.ts b/packages/kbn-generate-csv/src/generate_csv.ts index 18ee2cdb0005a..c59e4fd40aafb 100644 --- a/packages/kbn-generate-csv/src/generate_csv.ts +++ b/packages/kbn-generate-csv/src/generate_csv.ts @@ -21,9 +21,9 @@ import type { } from '@kbn/field-formats-plugin/common'; import { AuthenticationExpiredError, + byteSizeValueToNumber, CancellationToken, ReportingError, - byteSizeValueToNumber, } from '@kbn/reporting-common'; import type { TaskInstanceFields, TaskRunResult } from '@kbn/reporting-common/types'; import type { ReportingConfigType } from '@kbn/reporting-server'; @@ -63,7 +63,6 @@ export class CsvGenerator { private logger: Logger, private stream: Writable ) {} - /* * Load field formats for each field in the list */ @@ -180,9 +179,9 @@ export class CsvGenerator { /* * Intrinsically, generating the rows is a synchronous process. Awaiting - * on a setImmediate call here partititions what could be a very long and - * CPU-intenstive synchronous process into asychronous processes. This - * give NodeJS to process other asychronous events that wait on the Event + * on a setImmediate call here partitions what could be a very long and + * CPU-intensive synchronous process into asynchronous processes. This + * give NodeJS to process other asynchronous events that wait on the Event * Loop. * * See: https://nodejs.org/en/docs/guides/dont-block-the-event-loop/ @@ -225,7 +224,13 @@ export class CsvGenerator { public async generateData(): Promise { const logger = this.logger; const [settings, searchSource] = await Promise.all([ - getExportSettings(this.clients.uiSettings, this.config, this.job.browserTimezone, logger), + getExportSettings( + this.clients.uiSettings, + this.taskInstanceFields, + this.config, + this.job.browserTimezone, + logger + ), this.dependencies.searchSourceStart.create(this.job.searchSource), ]); @@ -252,15 +257,30 @@ export class CsvGenerator { let totalRecords: number | undefined; let reportingError: undefined | ReportingError; + const abortController = new AbortController(); + this.cancellationToken.on(() => abortController.abort()); + // use a class to internalize the paging strategy let cursor: SearchCursor; if (this.job.pagingStrategy === 'scroll') { // Optional strategy: scan-and-scroll - cursor = new SearchCursorScroll(indexPatternTitle, settings, this.clients, this.logger); + cursor = new SearchCursorScroll( + indexPatternTitle, + settings, + this.clients, + abortController, + this.logger + ); logger.debug('Using search strategy: scroll'); } else { // Default strategy: point-in-time - cursor = new SearchCursorPit(indexPatternTitle, settings, this.clients, this.logger); + cursor = new SearchCursorPit( + indexPatternTitle, + settings, + this.clients, + abortController, + this.logger + ); logger.debug('Using search strategy: pit'); } await cursor.initialize(); @@ -289,6 +309,7 @@ export class CsvGenerator { if (this.cancellationToken.isCancelled()) { break; } + searchSource.setField('size', settings.scroll.size); let results: estypes.SearchResponse | undefined; @@ -406,7 +427,7 @@ export class CsvGenerator { /* * Add the errors into the CSV content. This makes error messages more * discoverable. When the export was automated or triggered by an API - * call or is automated, the user doesn't necesssarily go through the + * call or is automated, the user doesn't necessarily go through the * Kibana UI to download the export and might not otherwise see the * error message. */ diff --git a/packages/kbn-generate-csv/src/generate_csv_esql.test.ts b/packages/kbn-generate-csv/src/generate_csv_esql.test.ts index 3ef6e3b2318a9..3e925893d37e4 100644 --- a/packages/kbn-generate-csv/src/generate_csv_esql.test.ts +++ b/packages/kbn-generate-csv/src/generate_csv_esql.test.ts @@ -8,6 +8,7 @@ import * as Rx from 'rxjs'; import type { Writable } from 'stream'; +import { add, type Duration } from 'date-fns'; import { errors as esErrors } from '@elastic/elasticsearch'; import type { IScopedClusterClient, IUiSettingsClient, Logger } from '@kbn/core/server'; @@ -20,9 +21,9 @@ import { import { IKibanaSearchResponse } from '@kbn/data-plugin/common'; import { IScopedSearchClient } from '@kbn/data-plugin/server'; import { dataPluginMock } from '@kbn/data-plugin/server/mocks'; -import type { ESQLSearchReponse } from '@kbn/es-types'; import { CancellationToken } from '@kbn/reporting-common'; import type { ReportingConfigType } from '@kbn/reporting-server'; +import type { ESQLSearchReponse as ESQLSearchResponse } from '@kbn/es-types'; import { UI_SETTINGS_CSV_QUOTE_VALUES, UI_SETTINGS_CSV_SEPARATOR, @@ -37,6 +38,8 @@ const createMockJob = ( query: { esql: '' }, }); +const mockTaskInstanceFields = { startedAt: null, retryAt: null }; + describe('CsvESQLGenerator', () => { let mockEsClient: IScopedClusterClient; let mockDataClient: IScopedSearchClient; @@ -47,20 +50,20 @@ describe('CsvESQLGenerator', () => { let content: string; const getMockRawResponse = ( - esqlResponse: ESQLSearchReponse = { + esqlResponse: ESQLSearchResponse = { columns: [], values: [], } - ): ESQLSearchReponse => esqlResponse; + ): ESQLSearchResponse => esqlResponse; const mockDataClientSearchDefault = jest.fn().mockImplementation( - (): Rx.Observable> => + (): Rx.Observable> => Rx.of({ rawResponse: getMockRawResponse(), }) ); - const mockSearchResponse = (response: ESQLSearchReponse) => { + const mockSearchResponse = (response: ESQLSearchResponse) => { mockDataClient.search = jest.fn().mockImplementation(() => Rx.of({ rawResponse: getMockRawResponse(response), @@ -105,6 +108,7 @@ describe('CsvESQLGenerator', () => { const generateCsv = new CsvESQLGenerator( createMockJob({ columns: ['date', 'ip', 'message'] }), mockConfig, + mockTaskInstanceFields, { es: mockEsClient, data: mockDataClient, @@ -136,6 +140,7 @@ describe('CsvESQLGenerator', () => { const generateCsv = new CsvESQLGenerator( createMockJob(), mockConfig, + mockTaskInstanceFields, { es: mockEsClient, data: mockDataClient, @@ -163,6 +168,7 @@ describe('CsvESQLGenerator', () => { const generateCsv = new CsvESQLGenerator( createMockJob(), mockConfig, + mockTaskInstanceFields, { es: mockEsClient, data: mockDataClient, @@ -192,6 +198,7 @@ describe('CsvESQLGenerator', () => { const generateCsv = new CsvESQLGenerator( createMockJob(), mockConfig, + mockTaskInstanceFields, { es: mockEsClient, data: mockDataClient, @@ -211,6 +218,189 @@ describe('CsvESQLGenerator', () => { `); }); + describe('"auto" scroll duration config', () => { + const getTaskInstanceFields = (intervalFromNow: Duration) => { + const now = new Date(Date.now()); + return { startedAt: now, retryAt: add(now, intervalFromNow) }; + }; + + let mockConfigWithAutoScrollDuration: ReportingConfigType['csv']; + let mockDataClientSearchFn: jest.MockedFunction; + + beforeEach(() => { + mockConfigWithAutoScrollDuration = { + ...mockConfig, + scroll: { + ...mockConfig.scroll, + duration: 'auto', + }, + }; + + mockDataClientSearchFn = jest.fn(); + + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.clearAllTimers(); + jest.useRealTimers(); + + mockDataClientSearchFn.mockRestore(); + }); + + it('csv gets generated if search resolves without errors before the computed timeout value passed to the search data client elapses', async () => { + const timeFromNowInMs = 4 * 60 * 1000; + + const taskInstanceFields = getTaskInstanceFields({ + seconds: timeFromNowInMs / 1000, + }); + + mockDataClientSearchFn.mockImplementation((_, options) => { + const getSearchResult = () => { + const queuedAt = Date.now(); + + return new Promise>>( + (resolve, reject) => { + setTimeout(() => { + if ( + new Date(Date.now()).getTime() - new Date(queuedAt).getTime() > + Number((options?.transport?.requestTimeout! as string).replace(/ms/, '')) + ) { + reject( + new esErrors.ResponseError({ statusCode: 408, meta: {} as any, warnings: [] }) + ); + } else { + resolve({ + rawResponse: getMockRawResponse({ + columns: [{ name: 'message', type: 'string' }], + values: Array(100).fill(['This is a great message!']), + }), + }); + } + }, timeFromNowInMs / 4); + } + ); + }; + + return Rx.defer(getSearchResult); + }); + + const generateCsvPromise = new CsvESQLGenerator( + createMockJob(), + mockConfigWithAutoScrollDuration, + taskInstanceFields, + { + es: mockEsClient, + data: { + ...mockDataClient, + search: mockDataClientSearchFn, + }, + uiSettings: uiSettingsClient, + }, + new CancellationToken(), + mockLogger, + stream + ).generateData(); + + await jest.advanceTimersByTimeAsync(timeFromNowInMs); + + expect(await generateCsvPromise).toEqual( + expect.objectContaining({ + warnings: [], + }) + ); + + expect(mockDataClientSearchFn).toBeCalledWith( + { params: { filter: undefined, locale: 'en', query: '' } }, + { + strategy: 'esql', + transport: { + requestTimeout: `${timeFromNowInMs}ms`, + }, + abortSignal: expect.any(AbortSignal), + } + ); + }); + + it('csv generation errors if search request does not resolve before the computed timeout value passed to the search data client elapses', async () => { + const timeFromNowInMs = 4 * 60 * 1000; + + const taskInstanceFields = getTaskInstanceFields({ + seconds: timeFromNowInMs / 1000, + }); + + const requestDuration = timeFromNowInMs + 1000; + + mockDataClientSearchFn.mockImplementation((_, options) => { + const getSearchResult = () => { + const queuedAt = Date.now(); + + return new Promise>>( + (resolve, reject) => { + setTimeout(() => { + if ( + new Date(Date.now()).getTime() - new Date(queuedAt).getTime() > + Number((options?.transport?.requestTimeout! as string).replace(/ms/, '')) + ) { + reject( + new esErrors.ResponseError({ statusCode: 408, meta: {} as any, warnings: [] }) + ); + } else { + resolve({ + rawResponse: getMockRawResponse({ + columns: [{ name: 'message', type: 'string' }], + values: Array(100).fill(['This is a great message!']), + }), + }); + } + }, requestDuration); + } + ); + }; + + return Rx.defer(getSearchResult); + }); + + const generateCsvPromise = new CsvESQLGenerator( + createMockJob(), + mockConfigWithAutoScrollDuration, + taskInstanceFields, + { + es: mockEsClient, + data: { + ...mockDataClient, + search: mockDataClientSearchFn, + }, + uiSettings: uiSettingsClient, + }, + new CancellationToken(), + mockLogger, + stream + ).generateData(); + + await jest.advanceTimersByTimeAsync(requestDuration); + + expect(await generateCsvPromise).toEqual( + expect.objectContaining({ + warnings: expect.arrayContaining([ + expect.stringContaining('Received a 408 response from Elasticsearch'), + ]), + }) + ); + + expect(mockDataClientSearchFn).toBeCalledWith( + { params: { filter: undefined, locale: 'en', query: '' } }, + { + strategy: 'esql', + transport: { + requestTimeout: `${timeFromNowInMs}ms`, + }, + abortSignal: expect.any(AbortSignal), + } + ); + }); + }); + describe('jobParams', () => { it('uses columns to select columns', async () => { mockSearchResponse({ @@ -225,6 +415,7 @@ describe('CsvESQLGenerator', () => { const generateCsv = new CsvESQLGenerator( createMockJob({ columns: ['message', 'date', 'something else'] }), mockConfig, + mockTaskInstanceFields, { es: mockEsClient, data: mockDataClient, @@ -259,6 +450,7 @@ describe('CsvESQLGenerator', () => { const generateCsv = new CsvESQLGenerator( createMockJob({ query, filters }), mockConfig, + mockTaskInstanceFields, { es: mockEsClient, data: mockDataClient, @@ -318,6 +510,7 @@ describe('CsvESQLGenerator', () => { const generateCsv = new CsvESQLGenerator( createMockJob(), mockConfig, + mockTaskInstanceFields, { es: mockEsClient, data: mockDataClient, @@ -347,6 +540,7 @@ describe('CsvESQLGenerator', () => { const generateCsv = new CsvESQLGenerator( createMockJob(), mockConfig, + mockTaskInstanceFields, { es: mockEsClient, data: mockDataClient, @@ -385,6 +579,7 @@ describe('CsvESQLGenerator', () => { const generateCsv = new CsvESQLGenerator( createMockJob(), mockConfig, + mockTaskInstanceFields, { es: mockEsClient, data: mockDataClient, @@ -413,6 +608,7 @@ describe('CsvESQLGenerator', () => { const generateCsv = new CsvESQLGenerator( createMockJob(), mockConfig, + mockTaskInstanceFields, { es: mockEsClient, data: mockDataClient, @@ -449,6 +645,7 @@ describe('CsvESQLGenerator', () => { const generateCsv = new CsvESQLGenerator( createMockJob(), mockConfig, + mockTaskInstanceFields, { es: mockEsClient, data: mockDataClient, diff --git a/packages/kbn-generate-csv/src/generate_csv_esql.ts b/packages/kbn-generate-csv/src/generate_csv_esql.ts index 567d4121af219..cea0838460ab5 100644 --- a/packages/kbn-generate-csv/src/generate_csv_esql.ts +++ b/packages/kbn-generate-csv/src/generate_csv_esql.ts @@ -30,6 +30,7 @@ import { } from '@kbn/reporting-common'; import type { TaskRunResult } from '@kbn/reporting-common/types'; import type { ReportingConfigType } from '@kbn/reporting-server'; +import { type TaskInstanceFields } from '@kbn/reporting-common/types'; import { zipObject } from 'lodash'; import { CONTENT_TYPE_CSV } from '../constants'; @@ -58,6 +59,7 @@ export class CsvESQLGenerator { constructor( private job: JobParamsCsvESQL, private config: ReportingConfigType['csv'], + private taskInstanceFields: TaskInstanceFields, private clients: Clients, private cancellationToken: CancellationToken, private logger: Logger, @@ -67,6 +69,7 @@ export class CsvESQLGenerator { public async generateData(): Promise { const settings = await getExportSettings( this.clients.uiSettings, + this.taskInstanceFields, this.config, this.job.browserTimezone, this.logger @@ -111,7 +114,7 @@ export class CsvESQLGenerator { strategy: ESQL_SEARCH_STRATEGY, abortSignal: abortController.signal, transport: { - requestTimeout: settings.scroll.duration, + requestTimeout: settings.scroll.duration(this.taskInstanceFields), }, }) ); diff --git a/packages/kbn-generate-csv/src/lib/get_export_settings.test.ts b/packages/kbn-generate-csv/src/lib/get_export_settings.test.ts index a732bc73f5706..9a9b2e5ff3586 100644 --- a/packages/kbn-generate-csv/src/lib/get_export_settings.test.ts +++ b/packages/kbn-generate-csv/src/lib/get_export_settings.test.ts @@ -13,6 +13,8 @@ import { uiSettingsServiceMock, } from '@kbn/core/server/mocks'; import type { ReportingConfigType } from '@kbn/reporting-server'; +import type { TaskInstanceFields } from '@kbn/reporting-common/types'; +import { sub, add, type Duration } from 'date-fns'; import { UI_SETTINGS_CSV_QUOTE_VALUES, @@ -25,6 +27,7 @@ import { getExportSettings } from './get_export_settings'; describe('getExportSettings', () => { let uiSettingsClient: IUiSettingsClient; let config: ReportingConfigType['csv']; + let taskInstanceFields: TaskInstanceFields; const logger = loggingSystemMock.createLogger(); beforeEach(() => { @@ -38,6 +41,8 @@ describe('getExportSettings', () => { enablePanelActionDownload: true, }; + taskInstanceFields = { startedAt: null, retryAt: null }; + uiSettingsClient = uiSettingsServiceMock .createStartContract() .asScopedToClient(savedObjectsClientMock.create()); @@ -53,31 +58,42 @@ describe('getExportSettings', () => { return false; } - return 'helo world'; + return 'hello world'; }); }); test('getExportSettings: returns the expected result', async () => { - expect(await getExportSettings(uiSettingsClient, config, '', logger)).toMatchObject({ - bom: '', - checkForFormulas: true, - escapeFormulaValues: false, - includeFrozen: false, - maxConcurrentShardRequests: 5, - maxSizeBytes: 180000, - scroll: { - duration: '30s', - size: 500, - }, - separator: ',', - timezone: 'UTC', - }); + expect(await getExportSettings(uiSettingsClient, taskInstanceFields, config, '', logger)) + .toMatchInlineSnapshot(` + Object { + "bom": "", + "checkForFormulas": true, + "escapeFormulaValues": false, + "escapeValue": [Function], + "includeFrozen": false, + "maxConcurrentShardRequests": 5, + "maxSizeBytes": 180000, + "scroll": Object { + "duration": [Function], + "size": 500, + "strategy": "pit", + }, + "separator": ",", + "taskInstanceFields": Object { + "retryAt": null, + "startedAt": null, + }, + "timezone": "UTC", + } + `); }); test('does not add a default scroll strategy', async () => { // @ts-expect-error undefined isn't allowed config = { ...config, scroll: { strategy: undefined } }; - expect(await getExportSettings(uiSettingsClient, config, '', logger)).toMatchObject( + expect( + await getExportSettings(uiSettingsClient, taskInstanceFields, config, '', logger) + ).toMatchObject( expect.objectContaining({ scroll: expect.objectContaining({ strategy: undefined }) }) ); }); @@ -85,7 +101,9 @@ describe('getExportSettings', () => { test('passes the scroll=pit strategy through', async () => { config = { ...config, scroll: { ...config.scroll, strategy: 'pit' } }; - expect(await getExportSettings(uiSettingsClient, config, '', logger)).toMatchObject( + expect( + await getExportSettings(uiSettingsClient, taskInstanceFields, config, '', logger) + ).toMatchObject( expect.objectContaining({ scroll: expect.objectContaining({ strategy: 'pit' }) }) ); }); @@ -93,7 +111,9 @@ describe('getExportSettings', () => { test('passes the scroll=scroll strategy through', async () => { config = { ...config, scroll: { ...config.scroll, strategy: 'scroll' } }; - expect(await getExportSettings(uiSettingsClient, config, '', logger)).toMatchObject( + expect( + await getExportSettings(uiSettingsClient, taskInstanceFields, config, '', logger) + ).toMatchObject( expect.objectContaining({ scroll: expect.objectContaining({ strategy: 'scroll', @@ -103,7 +123,13 @@ describe('getExportSettings', () => { }); test('escapeValue function', async () => { - const { escapeValue } = await getExportSettings(uiSettingsClient, config, '', logger); + const { escapeValue } = await getExportSettings( + uiSettingsClient, + taskInstanceFields, + config, + '', + logger + ); expect(escapeValue(`test`)).toBe(`test`); expect(escapeValue(`this is, a test`)).toBe(`"this is, a test"`); expect(escapeValue(`"tet"`)).toBe(`"""tet"""`); @@ -119,7 +145,111 @@ describe('getExportSettings', () => { }); expect( - await getExportSettings(uiSettingsClient, config, '', logger).then(({ timezone }) => timezone) + await getExportSettings(uiSettingsClient, taskInstanceFields, config, '', logger).then( + ({ timezone }) => timezone + ) ).toBe(`America/Aruba`); }); + + describe('scroll duration function', () => { + let spiedDateNow: jest.Spied; + let mockedTaskInstanceFields: TaskInstanceFields; + const durationApart: Duration = { minutes: 5 }; + + beforeEach(() => { + const now = Date.now(); + + // freeze time for test + spiedDateNow = jest.spyOn(Date, 'now').mockReturnValue(now); + + mockedTaskInstanceFields = { + startedAt: sub(new Date(Date.now()), durationApart), + retryAt: add(new Date(Date.now()), durationApart), + }; + }); + + afterEach(() => { + spiedDateNow.mockRestore(); + }); + + it('returns its specified value when value is not auto', async () => { + const { scroll } = await getExportSettings( + uiSettingsClient, + taskInstanceFields, + config, + '', + logger + ); + + expect(scroll.duration(mockedTaskInstanceFields)).toBe(config.scroll.duration); + }); + + it('throws when the scroll duration config is auto and retryAt value of the taskInstanceField passed is falsy', async () => { + const configWithScrollAutoDuration = { + ...config, + scroll: { + ...config.scroll, + duration: 'auto', + }, + }; + + const { scroll } = await getExportSettings( + uiSettingsClient, + taskInstanceFields, + configWithScrollAutoDuration, + '', + logger + ); + + expect( + scroll.duration.bind(null, { startedAt: new Date(Date.now()), retryAt: null }) + ).toThrow(); + }); + + it('returns a value that is the difference of the current time from the value of retryAt provided in the passed taskInstanceFields', async () => { + const configWithScrollAutoDuration = { + ...config, + scroll: { + ...config.scroll, + duration: 'auto', + }, + }; + + const { scroll } = await getExportSettings( + uiSettingsClient, + taskInstanceFields, + configWithScrollAutoDuration, + '', + logger + ); + + expect(scroll.duration(mockedTaskInstanceFields)).toBe( + `${durationApart.minutes! * 60 * 1000}ms` + ); + }); + + it('returns 0 if current time exceeds the value of retryAt provided in the passed taskInstanceFields', async () => { + const configWithScrollAutoDuration = { + ...config, + scroll: { + ...config.scroll, + duration: 'auto', + }, + }; + + spiedDateNow.mockReturnValue( + add(mockedTaskInstanceFields.retryAt!, { minutes: 5 }).getTime() + ); + + const { scroll } = await getExportSettings( + uiSettingsClient, + taskInstanceFields, + configWithScrollAutoDuration, + '', + logger + ); + + expect(scroll.duration(mockedTaskInstanceFields)).toBe('0ms'); + }); + }); }); diff --git a/packages/kbn-generate-csv/src/lib/get_export_settings.ts b/packages/kbn-generate-csv/src/lib/get_export_settings.ts index 20830a3c3c023..a3a8b5625bff3 100644 --- a/packages/kbn-generate-csv/src/lib/get_export_settings.ts +++ b/packages/kbn-generate-csv/src/lib/get_export_settings.ts @@ -10,7 +10,7 @@ import type { ByteSizeValue } from '@kbn/config-schema'; import type { IUiSettingsClient, Logger } from '@kbn/core/server'; import { createEscapeValue } from '@kbn/data-plugin/common'; import type { ReportingConfigType } from '@kbn/reporting-server'; - +import type { TaskInstanceFields } from '@kbn/reporting-common/types'; import { CSV_BOM_CHARS, UI_SETTINGS_CSV_QUOTE_VALUES, @@ -22,10 +22,14 @@ import { CsvPagingStrategy } from '../../types'; export interface CsvExportSettings { timezone: string; + taskInstanceFields: TaskInstanceFields; scroll: { strategy?: CsvPagingStrategy; size: number; - duration: string; + /** + * compute scroll duration, duration is returned in ms by default + */ + duration: (args: TaskInstanceFields, format?: 'ms' | 's') => string; }; bom: string; separator: string; @@ -39,6 +43,7 @@ export interface CsvExportSettings { export const getExportSettings = async ( client: IUiSettingsClient, + taskInstanceFields: TaskInstanceFields, config: ReportingConfigType['csv'], timezone: string | undefined, logger: Logger @@ -75,10 +80,33 @@ export const getExportSettings = async ( return { timezone: setTimezone, + taskInstanceFields, scroll: { strategy: config.scroll.strategy as CsvPagingStrategy, size: config.scroll.size, - duration: config.scroll.duration, + duration: ({ retryAt }, format = 'ms') => { + if (config.scroll.duration !== 'auto') { + return config.scroll.duration; + } + + if (!retryAt) { + throw new Error( + 'config "xpack.reporting.csv.scroll.duration" of "auto" mandates that the task instance field passed specifies a retryAt value' + ); + } + + const now = new Date(Date.now()).getTime(); + const timeTillRetry = new Date(retryAt).getTime(); + + if (now >= timeTillRetry) { + return `0${format}`; + } + + const _duration = timeTillRetry - now; + const result = format === 'ms' ? `${_duration}ms` : `${_duration / 1000}s`; + logger.debug(`using timeout duration of ${result} for csv scroll`); + return result; + }, }, bom, includeFrozen, diff --git a/packages/kbn-generate-csv/src/lib/search_cursor.ts b/packages/kbn-generate-csv/src/lib/search_cursor.ts index 9632564492e3e..834f5d6fb2fbc 100644 --- a/packages/kbn-generate-csv/src/lib/search_cursor.ts +++ b/packages/kbn-generate-csv/src/lib/search_cursor.ts @@ -23,7 +23,7 @@ export interface SearchCursorClients { export type SearchCursorSettings = Pick< CsvExportSettings, - 'scroll' | 'includeFrozen' | 'maxConcurrentShardRequests' + 'scroll' | 'includeFrozen' | 'maxConcurrentShardRequests' | 'taskInstanceFields' >; export abstract class SearchCursor { @@ -33,6 +33,7 @@ export abstract class SearchCursor { protected indexPatternTitle: string, protected settings: SearchCursorSettings, protected clients: SearchCursorClients, + protected abortController: AbortController, protected logger: Logger ) {} diff --git a/packages/kbn-generate-csv/src/lib/search_cursor_pit.test.ts b/packages/kbn-generate-csv/src/lib/search_cursor_pit.test.ts index c76a66a983893..4fb3ccda06c63 100644 --- a/packages/kbn-generate-csv/src/lib/search_cursor_pit.test.ts +++ b/packages/kbn-generate-csv/src/lib/search_cursor_pit.test.ts @@ -24,11 +24,12 @@ describe('CSV Export Search Cursor', () => { beforeEach(async () => { settings = { scroll: { - duration: '10m', + duration: jest.fn(() => '10m'), size: 500, }, includeFrozen: false, maxConcurrentShardRequests: 5, + taskInstanceFields: { startedAt: null, retryAt: null }, }; es = elasticsearchServiceMock.createScopedClusterClient(); @@ -37,7 +38,13 @@ describe('CSV Export Search Cursor', () => { logger = loggingSystemMock.createLogger(); - cursor = new SearchCursorPit('test-index-pattern-string', settings, { data, es }, logger); + cursor = new SearchCursorPit( + 'test-index-pattern-string', + settings, + { data, es }, + new AbortController(), + logger + ); const openPointInTimeSpy = jest // @ts-expect-error create spy on private method diff --git a/packages/kbn-generate-csv/src/lib/search_cursor_pit.ts b/packages/kbn-generate-csv/src/lib/search_cursor_pit.ts index 110e15ca8a602..a7099f8419339 100644 --- a/packages/kbn-generate-csv/src/lib/search_cursor_pit.ts +++ b/packages/kbn-generate-csv/src/lib/search_cursor_pit.ts @@ -24,9 +24,10 @@ export class SearchCursorPit extends SearchCursor { indexPatternTitle: string, settings: SearchCursorSettings, clients: SearchCursorClients, + abortController: AbortController, logger: Logger ) { - super(indexPatternTitle, settings, clients, logger); + super(indexPatternTitle, settings, clients, abortController, logger); } /** @@ -37,7 +38,7 @@ export class SearchCursorPit extends SearchCursor { } private async openPointInTime() { - const { includeFrozen, maxConcurrentShardRequests, scroll } = this.settings; + const { includeFrozen, maxConcurrentShardRequests, scroll, taskInstanceFields } = this.settings; let pitId: string | undefined; @@ -47,13 +48,14 @@ export class SearchCursorPit extends SearchCursor { const response = await this.clients.es.asCurrentUser.openPointInTime( { index: this.indexPatternTitle, - keep_alive: scroll.duration, + keep_alive: scroll.duration(taskInstanceFields), ignore_unavailable: true, // @ts-expect-error ignore_throttled is not in the type definition, but it is accepted by es ignore_throttled: includeFrozen ? false : undefined, // "true" will cause deprecation warnings logged in ES }, { - requestTimeout: scroll.duration, + signal: this.abortController.signal, + requestTimeout: scroll.duration(taskInstanceFields), maxRetries: 0, maxConcurrentShardRequests, } @@ -73,7 +75,7 @@ export class SearchCursorPit extends SearchCursor { } private async searchWithPit(searchBody: SearchRequest) { - const { maxConcurrentShardRequests, scroll } = this.settings; + const { maxConcurrentShardRequests, scroll, taskInstanceFields } = this.settings; const searchParamsPit = { params: { @@ -85,22 +87,25 @@ export class SearchCursorPit extends SearchCursor { return await lastValueFrom( this.clients.data.search(searchParamsPit, { strategy: ES_SEARCH_STRATEGY, + abortSignal: this.abortController.signal, transport: { maxRetries: 0, // retrying reporting jobs is handled in the task manager scheduling logic - requestTimeout: scroll.duration, + requestTimeout: scroll.duration(taskInstanceFields), }, }) ); } public async getPage(searchSource: ISearchSource) { + const { scroll, taskInstanceFields } = this.settings; + if (!this.cursorId) { throw new Error(`No access to valid PIT ID!`); } searchSource.setField('pit', { id: this.cursorId, - keep_alive: this.settings.scroll.duration, + keep_alive: scroll.duration(taskInstanceFields), }); const searchAfter = this.getSearchAfter(); diff --git a/packages/kbn-generate-csv/src/lib/search_cursor_scroll.test.ts b/packages/kbn-generate-csv/src/lib/search_cursor_scroll.test.ts index 4b4ae4a05f4ec..6d36ee64d5761 100644 --- a/packages/kbn-generate-csv/src/lib/search_cursor_scroll.test.ts +++ b/packages/kbn-generate-csv/src/lib/search_cursor_scroll.test.ts @@ -24,10 +24,11 @@ describe('CSV Export Search Cursor', () => { beforeEach(async () => { settings = { scroll: { - duration: '10m', + duration: jest.fn(() => '10m'), size: 500, }, includeFrozen: false, + taskInstanceFields: { startedAt: null, retryAt: null }, maxConcurrentShardRequests: 5, }; @@ -37,7 +38,14 @@ describe('CSV Export Search Cursor', () => { logger = loggingSystemMock.createLogger(); - cursor = new SearchCursorScroll('test-index-pattern-string', settings, { data, es }, logger); + cursor = new SearchCursorScroll( + 'test-index-pattern-string', + settings, + { data, es }, + new AbortController(), + logger + ); + await cursor.initialize(); }); diff --git a/packages/kbn-generate-csv/src/lib/search_cursor_scroll.ts b/packages/kbn-generate-csv/src/lib/search_cursor_scroll.ts index 6e2bc2100a14a..8b8e41da39700 100644 --- a/packages/kbn-generate-csv/src/lib/search_cursor_scroll.ts +++ b/packages/kbn-generate-csv/src/lib/search_cursor_scroll.ts @@ -23,22 +23,23 @@ export class SearchCursorScroll extends SearchCursor { indexPatternTitle: string, settings: SearchCursorSettings, clients: SearchCursorClients, + abortController: AbortController, logger: Logger ) { - super(indexPatternTitle, settings, clients, logger); + super(indexPatternTitle, settings, clients, abortController, logger); } // The first search query begins the scroll context in ES public async initialize() {} private async scan(searchBody: SearchRequest) { - const { includeFrozen, maxConcurrentShardRequests, scroll } = this.settings; + const { includeFrozen, maxConcurrentShardRequests, scroll, taskInstanceFields } = this.settings; const searchParamsScan = { params: { body: searchBody, index: this.indexPatternTitle, - scroll: scroll.duration, + scroll: scroll.duration(taskInstanceFields), size: scroll.size, ignore_throttled: includeFrozen ? false : undefined, // "true" will cause deprecation warnings logged in ES max_concurrent_shard_requests: maxConcurrentShardRequests, @@ -48,21 +49,23 @@ export class SearchCursorScroll extends SearchCursor { return await lastValueFrom( this.clients.data.search(searchParamsScan, { strategy: ES_SEARCH_STRATEGY, + abortSignal: this.abortController.signal, transport: { maxRetries: 0, // retrying reporting jobs is handled in the task manager scheduling logic - requestTimeout: scroll.duration, + requestTimeout: scroll.duration(taskInstanceFields), }, }) ); } private async scroll() { - const { duration } = this.settings.scroll; + const { scroll, taskInstanceFields } = this.settings; return await this.clients.es.asCurrentUser.scroll( - { scroll: duration, scroll_id: this.cursorId }, + { scroll: scroll.duration(taskInstanceFields), scroll_id: this.cursorId }, { + signal: this.abortController.signal, maxRetries: 0, // retrying reporting jobs is handled in the task manager scheduling logic - requestTimeout: duration, + requestTimeout: scroll.duration(taskInstanceFields), } ); } diff --git a/packages/kbn-guided-onboarding/src/components/landing_page/guide/guide_card.tsx b/packages/kbn-guided-onboarding/src/components/landing_page/guide/guide_card.tsx index 1194fc5a8d259..a9ea5fbab5b6e 100644 --- a/packages/kbn-guided-onboarding/src/components/landing_page/guide/guide_card.tsx +++ b/packages/kbn-guided-onboarding/src/components/landing_page/guide/guide_card.tsx @@ -8,14 +8,7 @@ import React, { Fragment, useCallback, useState } from 'react'; -import { - EuiCard, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiTextColor, - useEuiTheme, -} from '@elastic/eui'; +import { EuiCard, EuiFlexGroup, EuiIcon, EuiTextColor, useEuiTheme } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; @@ -138,7 +131,7 @@ export const GuideCard = ({ const cardCss = css` position: relative; - height: 125px; + height: 150px; width: 380px; .euiCard__top { margin-block-end: 8px; @@ -149,7 +142,7 @@ export const GuideCard = ({ } @media (min-width: 768px) and (max-width: 1210px) { max-width: 230px; - height: 175px; + height: 200px; justify-content: center; } `; @@ -175,16 +168,12 @@ export const GuideCard = ({ )} {isComplete && ( - - - - - - {i18n.translate('guidedOnboardingPackage.gettingStarted.cards.completeLabel', { - defaultMessage: 'Guide complete', - })} - - + + + {i18n.translate('guidedOnboardingPackage.gettingStarted.cards.completeLabel', { + defaultMessage: 'Guide complete', + })} + )} diff --git a/packages/kbn-mock-idp-plugin/server/index.ts b/packages/kbn-mock-idp-plugin/server/index.ts index 81f6713e675fe..33e5556142155 100644 --- a/packages/kbn-mock-idp-plugin/server/index.ts +++ b/packages/kbn-mock-idp-plugin/server/index.ts @@ -6,5 +6,17 @@ * Side Public License, v 1. */ +import { offeringBasedSchema, schema } from '@kbn/config-schema'; + export type { CreateSAMLResponseParams } from './plugin'; export { plugin } from './plugin'; + +export const config = { + schema: schema.object({ + // The plugin should only be enabled in Serverless. + enabled: offeringBasedSchema({ + serverless: schema.boolean({ defaultValue: true }), + options: { defaultValue: false }, + }), + }), +}; diff --git a/packages/kbn-reporting/export_types/csv/csv_v2.ts b/packages/kbn-reporting/export_types/csv/csv_v2.ts index 3bea7f03375bb..aeaee5b98d7cd 100644 --- a/packages/kbn-reporting/export_types/csv/csv_v2.ts +++ b/packages/kbn-reporting/export_types/csv/csv_v2.ts @@ -147,6 +147,7 @@ export class CsvV2ExportType extends ExportType< ...job, }, csvConfig, + taskInstanceFields, clients, cancellationToken, logger, diff --git a/packages/kbn-reporting/server/config_schema.ts b/packages/kbn-reporting/server/config_schema.ts index a823babde8f00..0c602b1bb340b 100644 --- a/packages/kbn-reporting/server/config_schema.ts +++ b/packages/kbn-reporting/server/config_schema.ts @@ -74,10 +74,10 @@ const CsvSchema = schema.object({ { defaultValue: 'pit' } ), duration: schema.string({ - defaultValue: '30s', // this value is passed directly to ES, so string only format is preferred + defaultValue: '30s', // values other than "auto" are passed directly to ES, so string only format is preferred validate(value) { - if (!/^[0-9]+(d|h|m|s|ms|micros|nanos)$/.test(value)) { - return 'must be a duration string'; + if (!/(^[0-9]+(d|h|m|s|ms|micros|nanos)|auto)$/.test(value)) { + return 'must be either "auto" or a duration string'; } }, }), diff --git a/packages/kbn-search-connectors/components/configuration/connector_configuration.tsx b/packages/kbn-search-connectors/components/configuration/connector_configuration.tsx index b3fc911460bf7..1377626f3cd58 100644 --- a/packages/kbn-search-connectors/components/configuration/connector_configuration.tsx +++ b/packages/kbn-search-connectors/components/configuration/connector_configuration.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { createContext, useEffect, useState } from 'react'; +import React, { createContext, useEffect, useRef, useState } from 'react'; import { EuiButton, @@ -20,8 +20,15 @@ import { import { i18n } from '@kbn/i18n'; +import { isDeepEqual } from 'react-use/lib/util'; import { sortAndFilterConnectorConfiguration } from '../../utils/connector_configuration_utils'; -import { Connector, ConnectorConfigProperties, ConnectorStatus, FeatureName } from '../..'; +import { + Connector, + ConnectorConfigProperties, + ConnectorConfiguration, + ConnectorStatus, + FeatureName, +} from '../..'; import { ConnectorConfigurationForm } from './connector_configuration_form'; @@ -82,6 +89,7 @@ export const ConnectorConfigurationComponent: React.FC { + const configurationRef = useRef({}); const { configuration, error, @@ -95,7 +103,10 @@ export const ConnectorConfigurationComponent: React.FC { - setIsEditing(false); + if (!isDeepEqual(configuration, configurationRef.current)) { + configurationRef.current = configuration; + setIsEditing(false); + } }, [configuration]); useEffect(() => { diff --git a/packages/kbn-search-connectors/lib/update_connector_service_type.test.ts b/packages/kbn-search-connectors/lib/update_connector_service_type.test.ts new file mode 100644 index 0000000000000..9a452e982430d --- /dev/null +++ b/packages/kbn-search-connectors/lib/update_connector_service_type.test.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ElasticsearchClient } from '@kbn/core/server'; + +import { errors } from '@elastic/elasticsearch'; + +import { updateConnectorServiceType } from './update_connector_service_type'; + +describe('updateConnectorServiceType lib function', () => { + const mockClient = { + transport: { + request: jest.fn(), + }, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should update connector service type', async () => { + mockClient.transport.request.mockImplementation(() => ({ result: 'updated' })); + + const connectorId = 'connectorId'; + const serviceType = 'new-service-type'; + + const result = await updateConnectorServiceType( + mockClient as unknown as ElasticsearchClient, + connectorId, + serviceType + ); + expect(result).toEqual({ result: 'updated' }); + + expect(mockClient.transport.request).toHaveBeenNthCalledWith(1, { + method: 'PUT', + path: `/_connector/${connectorId}/_configuration`, + body: { + configuration: {}, + }, + }); + expect(mockClient.transport.request).toHaveBeenNthCalledWith(2, { + method: 'PUT', + path: `/_connector/${connectorId}/_service_type`, + body: { + service_type: serviceType, + }, + }); + expect(mockClient.transport.request).toHaveBeenCalledTimes(2); + }); + + it('should not index document if there is no connector', async () => { + mockClient.transport.request.mockImplementationOnce(() => { + return Promise.reject( + new errors.ResponseError({ + statusCode: 404, + body: { + error: { + type: `document_missing_exception`, + }, + }, + } as any) + ); + }); + await expect( + updateConnectorServiceType( + mockClient as unknown as ElasticsearchClient, + 'connectorId', + 'new-service-type' + ) + ).rejects.toEqual( + new errors.ResponseError({ + statusCode: 404, + body: { + error: { + type: `document_missing_exception`, + }, + }, + } as any) + ); + }); +}); diff --git a/packages/kbn-search-connectors/lib/update_connector_service_type.ts b/packages/kbn-search-connectors/lib/update_connector_service_type.ts index 3040918a75b95..70d1105e69c3c 100644 --- a/packages/kbn-search-connectors/lib/update_connector_service_type.ts +++ b/packages/kbn-search-connectors/lib/update_connector_service_type.ts @@ -6,42 +6,29 @@ * Side Public License, v 1. */ +import { Result } from '@elastic/elasticsearch/lib/api/types'; import { ElasticsearchClient } from '@kbn/core/server'; -import { i18n } from '@kbn/i18n'; - -import { CONNECTORS_INDEX, fetchConnectorById } from '..'; - -import { ConnectorDocument, ConnectorStatus } from '../types/connectors'; export const updateConnectorServiceType = async ( client: ElasticsearchClient, connectorId: string, serviceType: string ) => { - const connectorResult = await fetchConnectorById(client, connectorId); - - if (connectorResult?.value) { - const result = await client.index({ - document: { - ...connectorResult.value, - configuration: {}, - service_type: serviceType, - status: - connectorResult.value.status === ConnectorStatus.CREATED - ? ConnectorStatus.CREATED - : ConnectorStatus.NEEDS_CONFIGURATION, - }, - id: connectorId, - index: CONNECTORS_INDEX, - if_seq_no: connectorResult.seqNo, - if_primary_term: connectorResult.primaryTerm, - }); - return result; - } else { - throw new Error( - i18n.translate('searchConnectors.server.connectors.serviceType.error', { - defaultMessage: 'Could not find document', - }) - ); - } + // First clear connector configuration + await client.transport.request({ + method: 'PUT', + path: `/_connector/${connectorId}/_configuration`, + body: { + configuration: {}, + }, + }); + // Then update service type, on startup connector framework + // will populate missing config fields + return await client.transport.request({ + method: 'PUT', + path: `/_connector/${connectorId}/_service_type`, + body: { + service_type: serviceType, + }, + }); }; diff --git a/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.test.tsx b/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.test.tsx index e6221c677b9d5..1ebd2b5a18174 100644 --- a/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.test.tsx +++ b/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.test.tsx @@ -237,6 +237,26 @@ describe('Group Selector Hooks', () => { ); }); + it('On group change, unselected group, does not sends telemetry', () => { + const testGroup = { + [groupingId]: { + ...defaultGroup, + options: defaultGroupingOptions, + activeGroups: ['host.name', customField], + }, + }; + const { result } = renderHook((props) => useGetGroupSelector(props), { + initialProps: { + ...defaultArgs, + groupingState: { + groupById: testGroup, + }, + }, + }); + act(() => result.current.props.onGroupChange(customField)); + expect(defaultArgs.tracker).not.toHaveBeenCalled(); + }); + it('On group change, executes callback', () => { const testGroup = { [groupingId]: { @@ -258,6 +278,32 @@ describe('Group Selector Hooks', () => { expect(defaultArgs.onGroupChange).toHaveBeenCalledWith({ tableId: groupingId, groupByField: customField, + groupByFields: ['host.name', customField], + }); + }); + + it('On group change, unselected group, executes callback', () => { + const testGroup = { + [groupingId]: { + ...defaultGroup, + options: defaultGroupingOptions, + activeGroups: ['host.name', customField], + }, + }; + const { result } = renderHook((props) => useGetGroupSelector(props), { + initialProps: { + ...defaultArgs, + groupingState: { + groupById: testGroup, + }, + }, + }); + act(() => result.current.props.onGroupChange(customField)); + expect(defaultArgs.onGroupChange).toHaveBeenCalledTimes(1); + expect(defaultArgs.onGroupChange).toHaveBeenCalledWith({ + tableId: groupingId, + groupByField: customField, + groupByFields: ['host.name'], }); }); diff --git a/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx b/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx index d3e518a48fe59..bec790ca8cce0 100644 --- a/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx +++ b/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx @@ -24,7 +24,11 @@ export interface UseGetGroupSelectorArgs { groupingState: GroupMap; maxGroupingLevels?: number; onOptionsChange?: (newOptions: GroupOption[]) => void; - onGroupChange?: (param: { groupByField: string; tableId: string }) => void; + onGroupChange?: (param: { + groupByField: string; + groupByFields: string[]; + tableId: string; + }) => void; tracker?: ( type: UiCounterMetricType, event: string | string[], @@ -112,33 +116,46 @@ export const useGetGroupSelector = ({ const onChange = useCallback( (groupSelection: string) => { + let newSelectedGroups: string[] = []; + let sendTelemetry = true; // Simulate a toggle behavior when maxGroupingLevels is 1 if (maxGroupingLevels === 1) { - setSelectedGroups([groupSelection]); + newSelectedGroups = [groupSelection]; } else { + // if the group is already selected, remove it if (selectedGroups.find((selected) => selected === groupSelection)) { + sendTelemetry = false; const groups = selectedGroups.filter((selectedGroup) => selectedGroup !== groupSelection); if (groups.length === 0) { - setSelectedGroups(['none']); + newSelectedGroups = ['none']; } else { - setSelectedGroups(groups); + newSelectedGroups = groups; } - return; + } else { + newSelectedGroups = isNoneGroup([groupSelection]) + ? [groupSelection] + : [ + ...selectedGroups.filter((selectedGroup) => selectedGroup !== 'none'), + groupSelection, + ]; } - - const newSelectedGroups = isNoneGroup([groupSelection]) - ? [groupSelection] - : [...selectedGroups.filter((selectedGroup) => selectedGroup !== 'none'), groupSelection]; - setSelectedGroups(newSelectedGroups); } - // built-in telemetry: UI-counter - tracker?.( - METRIC_TYPE.CLICK, - getTelemetryEvent.groupChanged({ groupingId, selected: groupSelection }) - ); + setSelectedGroups(newSelectedGroups); - onGroupChange?.({ tableId: groupingId, groupByField: groupSelection }); + if (sendTelemetry) { + // built-in telemetry: UI-counter + tracker?.( + METRIC_TYPE.CLICK, + getTelemetryEvent.groupChanged({ groupingId, selected: groupSelection }) + ); + } + + onGroupChange?.({ + tableId: groupingId, + groupByField: groupSelection, + groupByFields: newSelectedGroups, + }); }, [groupingId, maxGroupingLevels, onGroupChange, selectedGroups, setSelectedGroups, tracker] ); diff --git a/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.tsx b/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.tsx index 3ff2232acbd72..38ada10a74b9b 100644 --- a/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.tsx +++ b/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.tsx @@ -65,7 +65,11 @@ interface GroupingArgs { /** for tracking * @param param { groupByField: string; tableId: string } selected group and table id */ - onGroupChange?: (param: { groupByField: string; tableId: string }) => void; + onGroupChange?: (param: { + groupByField: string; + groupByFields: string[]; + tableId: string; + }) => void; onOptionsChange?: (options: GroupOption[]) => void; tracker?: ( type: UiCounterMetricType, diff --git a/packages/presentation/presentation_containers/interfaces/last_saved_state.ts b/packages/presentation/presentation_containers/interfaces/last_saved_state.ts index 0c7e83aef7bf3..7e9519dcbe315 100644 --- a/packages/presentation/presentation_containers/interfaces/last_saved_state.ts +++ b/packages/presentation/presentation_containers/interfaces/last_saved_state.ts @@ -30,7 +30,7 @@ export const getLastSavedStateSubjectForChild = StateType ): PublishingSubject | undefined => { if (!parentApi) return; - const fetchUnsavedChanges = (): StateType | undefined => { + const fetchLastSavedState = (): StateType | undefined => { if (!apiPublishesLastSavedState(parentApi)) return; const rawLastSavedState = parentApi.getLastSavedStateForChild(childId); if (rawLastSavedState === undefined) return; @@ -39,11 +39,11 @@ export const getLastSavedStateSubjectForChild = (fetchUnsavedChanges()); + const lastSavedStateForChild = new BehaviorSubject(fetchLastSavedState()); if (!apiPublishesLastSavedState(parentApi)) return; parentApi.lastSavedState .pipe( - map(() => fetchUnsavedChanges()), + map(() => fetchLastSavedState()), filter((rawLastSavedState) => rawLastSavedState !== undefined) ) .subscribe(lastSavedStateForChild); diff --git a/packages/presentation/presentation_containers/interfaces/presentation_container.ts b/packages/presentation/presentation_containers/interfaces/presentation_container.ts index c5c558dfe6227..a92c5af7cbd0a 100644 --- a/packages/presentation/presentation_containers/interfaces/presentation_container.ts +++ b/packages/presentation/presentation_containers/interfaces/presentation_container.ts @@ -11,11 +11,15 @@ import { PublishesLastSavedState } from './last_saved_state'; export interface PanelPackage { panelType: string; - initialState: unknown; + initialState?: object; } export type PresentationContainer = Partial & PublishesLastSavedState & { + addNewPanel: ( + panel: PanelPackage, + displaySuccessMessage?: boolean + ) => Promise; registerPanelApi: ( panelId: string, panelApi: ApiType @@ -31,7 +35,8 @@ export const apiIsPresentationContainer = ( return Boolean( (unknownApi as PresentationContainer)?.removePanel !== undefined && (unknownApi as PresentationContainer)?.registerPanelApi !== undefined && - (unknownApi as PresentationContainer)?.replacePanel !== undefined + (unknownApi as PresentationContainer)?.replacePanel !== undefined && + (unknownApi as PresentationContainer)?.addNewPanel !== undefined ); }; diff --git a/packages/presentation/presentation_containers/mocks.ts b/packages/presentation/presentation_containers/mocks.ts index 5ac45fc6049c2..6d4610075c9d5 100644 --- a/packages/presentation/presentation_containers/mocks.ts +++ b/packages/presentation/presentation_containers/mocks.ts @@ -11,9 +11,10 @@ import { PresentationContainer } from './interfaces/presentation_container'; export const getMockPresentationContainer = (): PresentationContainer => { return { - registerPanelApi: jest.fn(), removePanel: jest.fn(), + addNewPanel: jest.fn(), replacePanel: jest.fn(), + registerPanelApi: jest.fn(), lastSavedState: new Subject(), getLastSavedStateForChild: jest.fn(), }; diff --git a/packages/presentation/presentation_publishing/index.ts b/packages/presentation/presentation_publishing/index.ts index 61209d6635729..80d2c5870efbe 100644 --- a/packages/presentation/presentation_publishing/index.ts +++ b/packages/presentation/presentation_publishing/index.ts @@ -7,6 +7,10 @@ */ export interface EmbeddableApiContext { + /** + * TODO: once all actions are entirely decoupled from the embeddable system, this key should be renamed to "api" + * to reflect the fact that this context could contain any api. + */ embeddable: unknown; } diff --git a/packages/shared-ux/code_editor/impl/BUILD.bazel b/packages/shared-ux/code_editor/impl/BUILD.bazel index ad571cb379afd..24f18820496a4 100644 --- a/packages/shared-ux/code_editor/impl/BUILD.bazel +++ b/packages/shared-ux/code_editor/impl/BUILD.bazel @@ -25,7 +25,6 @@ BUNDLER_DEPS = [ "@npm//react", "@npm//tslib", "@npm//react-monaco-editor", - "@npm//react-resize-detector", ] js_library( diff --git a/packages/shared-ux/code_editor/impl/code_editor.stories.tsx b/packages/shared-ux/code_editor/impl/code_editor.stories.tsx index 38c063e4ebe2b..e4a78b328cbe3 100644 --- a/packages/shared-ux/code_editor/impl/code_editor.stories.tsx +++ b/packages/shared-ux/code_editor/impl/code_editor.stories.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React from 'react'; +import React, { useState } from 'react'; import { action } from '@storybook/addon-actions'; import { monaco as monacoEditor } from '@kbn/monaco'; @@ -32,7 +32,13 @@ const argTypes = mock.getArgumentTypes(); export const Basic = (params: CodeEditorStorybookParams) => { return ( - + ); }; @@ -199,3 +205,39 @@ export const HoverProvider = () => {
); }; + +export const AutomaticResize = (params: CodeEditorStorybookParams) => { + return ( +
+ +
+ ); +}; + +AutomaticResize.argTypes = argTypes; + +export const FitToContent = (params: CodeEditorStorybookParams) => { + const [value, setValue] = useState('hello'); + return ( + { + setValue(newValue); + action('on change'); + }} + value={value} + fitToContent={{ minLines: 3, maxLines: 5 }} + options={{ automaticLayout: true }} + /> + ); +}; + +FitToContent.argTypes = argTypes; diff --git a/packages/shared-ux/code_editor/impl/code_editor.tsx b/packages/shared-ux/code_editor/impl/code_editor.tsx index b41906d5ed456..286c3af06e0b2 100644 --- a/packages/shared-ux/code_editor/impl/code_editor.tsx +++ b/packages/shared-ux/code_editor/impl/code_editor.tsx @@ -7,7 +7,6 @@ */ import React, { useState, useRef, useCallback, useMemo, useEffect, KeyboardEvent } from 'react'; -import { useResizeDetector } from 'react-resize-detector'; import ReactMonacoEditor, { type MonacoEditorProps as ReactMonacoEditorProps, } from 'react-monaco-editor'; @@ -140,12 +139,21 @@ export interface CodeEditorProps { * Alternate text to display, when an attempt is made to edit read only content. (Defaults to "Cannot edit in read-only editor") */ readOnlyMessage?: string; + + /** + * Enables the editor to grow vertically to fit its content. + * This option overrides the `height` option. + */ + fitToContent?: { + minLines?: number; + maxLines?: number; + }; } export const CodeEditor: React.FC = ({ languageId, value, - onChange, + onChange: _onChange, width, height, options, @@ -168,6 +176,7 @@ export const CodeEditor: React.FC = ({ readOnlyMessage = i18n.translate('sharedUXPackages.codeEditor.readOnlyMessage', { defaultMessage: 'Cannot edit in read-only editor', }), + fitToContent, }) => { const { colorMode, euiTheme } = useEuiTheme(); const useDarkTheme = useDarkThemeProp ?? colorMode === 'DARK'; @@ -189,7 +198,7 @@ export const CodeEditor: React.FC = ({ const isReadOnly = options?.readOnly ?? false; - const _editor = useRef(null); + const [_editor, setEditor] = useState(null); const _placeholderWidget = useRef(null); const isSuggestionMenuOpen = useRef(false); const editorHint = useRef(null); @@ -197,21 +206,12 @@ export const CodeEditor: React.FC = ({ const [isHintActive, setIsHintActive] = useState(true); - const _updateDimensions = useCallback(() => { - _editor.current?.layout(); - }, []); - - useResizeDetector({ - handleWidth: true, - handleHeight: true, - onResize: _updateDimensions, - refreshMode: 'debounce', - }); + const onChange = useBug175684OnChange(_onChange); const startEditing = useCallback(() => { setIsHintActive(false); - _editor.current?.focus(); - }, []); + _editor?.focus(); + }, [_editor]); const stopEditing = useCallback(() => { setIsHintActive(true); @@ -391,8 +391,6 @@ export const CodeEditor: React.FC = ({ remeasureFonts(); - _editor.current = editor; - const textbox = editor.getDomNode()?.getElementsByTagName('textarea')[0]; if (textbox) { // Make sure the textarea is not directly accessible with TAB @@ -435,6 +433,7 @@ export const CodeEditor: React.FC = ({ } editorDidMount?.(editor); + setEditor(editor); }, [editorDidMount, onBlurMonaco, onKeydownMonaco, readOnlyMessage] ); @@ -454,16 +453,18 @@ export const CodeEditor: React.FC = ({ }, []); useEffect(() => { - if (placeholder && !value && _editor.current) { + if (placeholder && !value && _editor) { // Mounts editor inside constructor - _placeholderWidget.current = new PlaceholderWidget(placeholder, euiTheme, _editor.current); + _placeholderWidget.current = new PlaceholderWidget(placeholder, euiTheme, _editor); } return () => { _placeholderWidget.current?.dispose(); _placeholderWidget.current = null; }; - }, [placeholder, value, euiTheme]); + }, [placeholder, value, euiTheme, _editor]); + + useFitToContent({ editor: _editor, fitToContent, isFullScreen }); const { CopyButton } = useCopy({ isCopyable, value }); @@ -512,7 +513,7 @@ export const CodeEditor: React.FC = ({ value={value} onChange={onChange} width={isFullScreen ? '100vw' : width} - height={isFullScreen ? '100vh' : height} + height={isFullScreen ? '100vh' : fitToContent ? undefined : height} editorWillMount={_editorWillMount} editorDidMount={_editorDidMount} editorWillUnmount={_editorWillUnmount} @@ -640,3 +641,57 @@ const useCopy = ({ isCopyable, value }: { isCopyable: boolean; value: string }) return { showCopyButton, CopyButton }; }; + +const useFitToContent = ({ + editor, + fitToContent, + isFullScreen, +}: { + editor: monaco.editor.IStandaloneCodeEditor | null; + isFullScreen: boolean; + fitToContent?: { minLines?: number; maxLines?: number }; +}) => { + const isFitToContent = !!fitToContent; + const minLines = fitToContent?.minLines; + const maxLines = fitToContent?.maxLines; + useEffect(() => { + if (!editor) return; + if (isFullScreen) return; + if (!isFitToContent) return; + + const updateHeight = () => { + const contentHeight = editor.getContentHeight(); + const lineHeight = editor.getOption(monaco.editor.EditorOption.lineHeight); + const minHeight = (minLines ?? 1) * lineHeight; + let maxHeight = maxLines ? maxLines * lineHeight : contentHeight; + maxHeight = Math.max(minHeight, maxHeight); + editor.layout({ + height: Math.min(maxHeight, Math.max(minHeight, contentHeight)), + width: editor.getLayoutInfo().width, + }); + }; + updateHeight(); + const disposable = editor.onDidContentSizeChange(updateHeight); + return () => { + disposable.dispose(); + editor.layout(); // reset the layout that was controlled by the fitToContent + }; + }, [editor, isFitToContent, minLines, maxLines, isFullScreen]); +}; + +// https://github.com/elastic/kibana/issues/175684 +// 'react-monaco-editor' has a bug that it always calls the initial onChange callback, so the closure might become stale +// we work this around by calling the latest onChange from props +const useBug175684OnChange = (onChange: CodeEditorProps['onChange']) => { + const onChangePropRef = useRef(onChange); + useEffect(() => { + onChangePropRef.current = onChange; + }, [onChange]); + const onChangeWrapper = useCallback>((_value, event) => { + if (onChangePropRef.current) { + onChangePropRef.current(_value, event); + } + }, []); + + return onChangeWrapper; +}; diff --git a/packages/shared-ux/code_editor/mocks/monaco_mock/index.tsx b/packages/shared-ux/code_editor/mocks/monaco_mock/index.tsx index f5b2fc9ab4108..d9b2d4093e67f 100644 --- a/packages/shared-ux/code_editor/mocks/monaco_mock/index.tsx +++ b/packages/shared-ux/code_editor/mocks/monaco_mock/index.tsx @@ -106,7 +106,9 @@ export const MockedMonacoEditor = ({ className?: string; ['data-test-subj']?: string; }) => { - editorWillMount?.(monaco); + useComponentWillMount(() => { + editorWillMount?.(monaco); + }); useEffect(() => { editorDidMount?.( @@ -133,3 +135,11 @@ export const MockedMonacoEditor = ({ ); }; + +const useComponentWillMount = (cb: Function) => { + const willMount = React.useRef(true); + + if (willMount.current) cb(); + + willMount.current = false; +}; diff --git a/src/cli/serve/serve.js b/src/cli/serve/serve.js index 82dafb1ea3a12..ef1092ad892c0 100644 --- a/src/cli/serve/serve.js +++ b/src/cli/serve/serve.js @@ -112,12 +112,11 @@ export function applyConfigOverrides(rawConfig, opts, extraCliOptions) { if (opts.serverless) { setServerlessKibanaDevServiceAccountIfPossible(get, set, opts); - // Load mock identity provider plugin and configure realm if supported (ES only supports SAML when run with SSL) + // Configure realm if supported (ES only supports SAML when run with SSL) if (opts.ssl && MOCK_IDP_PLUGIN_SUPPORTED) { // Ensure the plugin is loaded in dynamically to exclude from production build // eslint-disable-next-line import/no-dynamic-require const { MOCK_IDP_REALM_NAME } = require(MOCK_IDP_PLUGIN_PATH); - const pluginPath = resolve(require.resolve(MOCK_IDP_PLUGIN_PATH), '..'); if (has('server.basePath')) { console.log( @@ -126,7 +125,6 @@ export function applyConfigOverrides(rawConfig, opts, extraCliOptions) { _.unset(rawConfig, 'server.basePath'); } - set('plugins.paths', _.compact([].concat(get('plugins.paths'), pluginPath))); set(`xpack.security.authc.providers.saml.${MOCK_IDP_REALM_NAME}`, { order: Number.MAX_SAFE_INTEGER, realm: MOCK_IDP_REALM_NAME, diff --git a/src/core/server/integration_tests/core_app/bundle_routes.test.ts b/src/core/server/integration_tests/core_app/bundle_routes.test.ts index af3782b015912..b53bc07a85492 100644 --- a/src/core/server/integration_tests/core_app/bundle_routes.test.ts +++ b/src/core/server/integration_tests/core_app/bundle_routes.test.ts @@ -17,7 +17,7 @@ import { HttpService } from '@kbn/core-http-server-internal'; import { createHttpServer } from '@kbn/core-http-server-mocks'; import { registerRouteForBundle, FileHashCache } from '@kbn/core-apps-server-internal'; -const buildNum = 1234; +const buildHash = 'buildHash'; const fooPluginFixture = resolve(__dirname, './__fixtures__/plugin/foo'); describe('bundle routes', () => { @@ -47,8 +47,8 @@ describe('bundle routes', () => { isDist, fileHashCache, bundlesPath: fooPluginFixture, - routePath: `/${buildNum}/bundles/plugin/foo/`, - publicPath: `/${buildNum}/bundles/plugin/foo/`, + routePath: `/${buildHash}/bundles/plugin/foo/`, + publicPath: `/${buildHash}/bundles/plugin/foo/`, }); }; @@ -62,7 +62,7 @@ describe('bundle routes', () => { await server.start(); const response = await supertest(innerServer.listener) - .get(`/${buildNum}/bundles/plugin/foo/image.png`) + .get(`/${buildHash}/bundles/plugin/foo/image.png`) .expect(200); const actualImage = await readFile(resolve(fooPluginFixture, 'image.png')); @@ -80,7 +80,7 @@ describe('bundle routes', () => { await server.start(); const response = await supertest(innerServer.listener) - .get(`/${buildNum}/bundles/plugin/foo/plugin.js`) + .get(`/${buildHash}/bundles/plugin/foo/plugin.js`) .expect(200); const actualFile = await readFile(resolve(fooPluginFixture, 'plugin.js')); @@ -98,7 +98,7 @@ describe('bundle routes', () => { await server.start(); await supertest(innerServer.listener) - .get(`/${buildNum}/bundles/plugin/foo/../outside_output.js`) + .get(`/${buildHash}/bundles/plugin/foo/../outside_output.js`) .expect(404); }); @@ -112,7 +112,7 @@ describe('bundle routes', () => { await server.start(); await supertest(innerServer.listener) - .get(`/${buildNum}/bundles/plugin/foo/missing.js`) + .get(`/${buildHash}/bundles/plugin/foo/missing.js`) .expect(404); }); @@ -126,7 +126,7 @@ describe('bundle routes', () => { await server.start(); const response = await supertest(innerServer.listener) - .get(`/${buildNum}/bundles/plugin/foo/gzip_chunk.js`) + .get(`/${buildHash}/bundles/plugin/foo/gzip_chunk.js`) .expect(200); expect(response.get('content-encoding')).toEqual('gzip'); @@ -151,7 +151,7 @@ describe('bundle routes', () => { await server.start(); const response = await supertest(innerServer.listener) - .get(`/${buildNum}/bundles/plugin/foo/gzip_chunk.js`) + .get(`/${buildHash}/bundles/plugin/foo/gzip_chunk.js`) .expect(200); expect(response.get('cache-control')).toEqual('max-age=31536000'); @@ -170,7 +170,7 @@ describe('bundle routes', () => { await server.start(); const response = await supertest(innerServer.listener) - .get(`/${buildNum}/bundles/plugin/foo/gzip_chunk.js`) + .get(`/${buildHash}/bundles/plugin/foo/gzip_chunk.js`) .expect(200); expect(response.get('cache-control')).toEqual('must-revalidate'); diff --git a/src/core/server/integration_tests/http/http_server.test.ts b/src/core/server/integration_tests/http/http_server.test.ts index eeb6b46c9ff46..f58bfba11186d 100644 --- a/src/core/server/integration_tests/http/http_server.test.ts +++ b/src/core/server/integration_tests/http/http_server.test.ts @@ -11,19 +11,21 @@ import supertest from 'supertest'; import moment from 'moment'; import { of } from 'rxjs'; import { ByteSizeValue } from '@kbn/config-schema'; -import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; import { Router } from '@kbn/core-http-router-server-internal'; import { HttpServer, HttpConfig } from '@kbn/core-http-server-internal'; +import { mockCoreContext } from '@kbn/core-base-server-mocks'; +import type { Logger } from '@kbn/logging'; describe('Http server', () => { let server: HttpServer; let config: HttpConfig; - let logger: ReturnType; + let logger: Logger; + let coreContext: ReturnType; const enhanceWithContext = (fn: (...args: any[]) => any) => fn.bind(null, {}); beforeEach(() => { - const loggingService = loggingSystemMock.create(); - logger = loggingSystemMock.createLogger(); + coreContext = mockCoreContext.create(); + logger = coreContext.logger.get(); config = { name: 'kibana', @@ -43,7 +45,7 @@ describe('Http server', () => { shutdownTimeout: moment.duration(5, 's'), } as any; - server = new HttpServer(loggingService, 'tests', of(config.shutdownTimeout)); + server = new HttpServer(coreContext, 'tests', of(config.shutdownTimeout)); }); describe('Graceful shutdown', () => { diff --git a/src/core/server/integration_tests/http/tls_config_reload.test.ts b/src/core/server/integration_tests/http/tls_config_reload.test.ts index 3603f76ebc670..ad2c530faae52 100644 --- a/src/core/server/integration_tests/http/tls_config_reload.test.ts +++ b/src/core/server/integration_tests/http/tls_config_reload.test.ts @@ -10,7 +10,6 @@ import supertest from 'supertest'; import { duration } from 'moment'; import { BehaviorSubject, of } from 'rxjs'; import { KBN_CERT_PATH, KBN_KEY_PATH, ES_KEY_PATH, ES_CERT_PATH } from '@kbn/dev-utils'; -import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; import { Router } from '@kbn/core-http-router-server-internal'; import { HttpServer, @@ -20,6 +19,8 @@ import { externalUrlConfig, } from '@kbn/core-http-server-internal'; import { isServerTLS, flattenCertificateChain, fetchPeerCertificate } from './tls_utils'; +import { mockCoreContext } from '@kbn/core-base-server-mocks'; +import type { Logger } from '@kbn/logging'; const CSP_CONFIG = cspConfig.schema.validate({}); const EXTERNAL_URL_CONFIG = externalUrlConfig.schema.validate({}); @@ -27,16 +28,16 @@ const enhanceWithContext = (fn: (...args: any[]) => any) => fn.bind(null, {}); describe('HttpServer - TLS config', () => { let server: HttpServer; - let logger: ReturnType; + let logger: Logger; beforeAll(() => { process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; }); beforeEach(() => { - const loggingService = loggingSystemMock.create(); - logger = loggingSystemMock.createLogger(); - server = new HttpServer(loggingService, 'tests', of(duration('1s'))); + const coreContext = mockCoreContext.create(); + logger = coreContext.logger.get(); + server = new HttpServer(coreContext, 'tests', of(duration('1s'))); }); it('supports dynamic reloading of the TLS configuration', async () => { diff --git a/src/core/server/integration_tests/status/routes/status.test.ts b/src/core/server/integration_tests/status/routes/status.test.ts index 0d7d6a84e2497..755b9fc9b46f6 100644 --- a/src/core/server/integration_tests/status/routes/status.test.ts +++ b/src/core/server/integration_tests/status/routes/status.test.ts @@ -83,6 +83,7 @@ describe('GET /api/status', () => { branch: 'xbranch', buildNum: 1234, buildSha: 'xsha', + buildShaShort: 'x', dist: true, version: '9.9.9-SNAPSHOT', buildDate: new Date('2023-05-15T23:12:09.000Z'), diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index 62dd66f63ec62..b76a9c37dd6da 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -98,6 +98,7 @@ function pluginInitializerContextMock(config: T = {} as T) { branch: 'branch', buildNum: 100, buildSha: 'buildSha', + buildShaShort: 'buildShaShort', dist: false, buildDate: new Date('2023-05-15T23:12:09.000Z'), buildFlavor: 'traditional', diff --git a/src/core/tsconfig.json b/src/core/tsconfig.json index 78219114a51df..06e6cf68d3a94 100644 --- a/src/core/tsconfig.json +++ b/src/core/tsconfig.json @@ -157,6 +157,7 @@ "@kbn/core-plugins-contracts-server", "@kbn/dev-utils", "@kbn/server-http-tools", + "@kbn/core-base-server-mocks", ], "exclude": [ "target/**/*", diff --git a/src/dev/build/args.test.ts b/src/dev/build/args.test.ts index e379ae02ceba3..1bb50cf3cd9c1 100644 --- a/src/dev/build/args.test.ts +++ b/src/dev/build/args.test.ts @@ -33,6 +33,7 @@ it('build default and oss dist for current platform, without packages, by defaul "createDebPackage": false, "createDockerCloud": false, "createDockerContexts": true, + "createDockerFIPS": false, "createDockerServerless": false, "createDockerUBI": false, "createDockerUbuntu": false, @@ -72,6 +73,7 @@ it('builds packages if --all-platforms is passed', () => { "createDebPackage": true, "createDockerCloud": true, "createDockerContexts": true, + "createDockerFIPS": true, "createDockerServerless": true, "createDockerUBI": true, "createDockerUbuntu": true, @@ -111,6 +113,7 @@ it('limits packages if --rpm passed with --all-platforms', () => { "createDebPackage": false, "createDockerCloud": false, "createDockerContexts": true, + "createDockerFIPS": false, "createDockerServerless": false, "createDockerUBI": false, "createDockerUbuntu": false, @@ -150,6 +153,7 @@ it('limits packages if --deb passed with --all-platforms', () => { "createDebPackage": true, "createDockerCloud": false, "createDockerContexts": true, + "createDockerFIPS": false, "createDockerServerless": false, "createDockerUBI": false, "createDockerUbuntu": false, @@ -190,6 +194,7 @@ it('limits packages if --docker passed with --all-platforms', () => { "createDebPackage": false, "createDockerCloud": true, "createDockerContexts": true, + "createDockerFIPS": true, "createDockerServerless": true, "createDockerUBI": true, "createDockerUbuntu": true, @@ -237,6 +242,7 @@ it('limits packages if --docker passed with --skip-docker-ubi and --all-platform "createDebPackage": false, "createDockerCloud": true, "createDockerContexts": true, + "createDockerFIPS": true, "createDockerServerless": true, "createDockerUBI": false, "createDockerUbuntu": true, @@ -277,6 +283,7 @@ it('limits packages if --all-platforms passed with --skip-docker-ubuntu', () => "createDebPackage": true, "createDockerCloud": true, "createDockerContexts": true, + "createDockerFIPS": true, "createDockerServerless": true, "createDockerUBI": true, "createDockerUbuntu": false, @@ -305,3 +312,44 @@ it('limits packages if --all-platforms passed with --skip-docker-ubuntu', () => } `); }); + +it('limits packages if --all-platforms passed with --skip-docker-fips', () => { + expect(readCliArgs(['node', 'scripts/build', '--all-platforms', '--skip-docker-fips'])) + .toMatchInlineSnapshot(` + Object { + "buildOptions": Object { + "buildCanvasShareableRuntime": true, + "createArchives": true, + "createCdnAssets": true, + "createDebPackage": true, + "createDockerCloud": true, + "createDockerContexts": true, + "createDockerFIPS": false, + "createDockerServerless": true, + "createDockerUBI": true, + "createDockerUbuntu": true, + "createGenericFolders": true, + "createPlatformFolders": true, + "createRpmPackage": true, + "dockerContextUseLocalArtifact": null, + "dockerCrossCompile": false, + "dockerNamespace": null, + "dockerPush": false, + "dockerTag": null, + "dockerTagQualifier": null, + "downloadCloudDependencies": true, + "downloadFreshNode": true, + "eprRegistry": "snapshot", + "initialize": true, + "isRelease": false, + "targetAllPlatforms": true, + "versionQualifier": "", + "withExamplePlugins": false, + "withTestPlugins": false, + }, + "log": , + "showHelp": false, + "unknownFlags": Array [], + } + `); +}); diff --git a/src/dev/build/args.ts b/src/dev/build/args.ts index 8f16d5ce571c0..0996a8688ef22 100644 --- a/src/dev/build/args.ts +++ b/src/dev/build/args.ts @@ -33,6 +33,7 @@ export function readCliArgs(argv: string[]) { 'skip-docker-ubuntu', 'skip-docker-cloud', 'skip-docker-serverless', + 'skip-docker-fips', 'release', 'skip-node-download', 'skip-cloud-dependencies-download', @@ -143,6 +144,7 @@ export function readCliArgs(argv: string[]) { isOsPackageDesired('docker-images') && !Boolean(flags['skip-docker-serverless']), createDockerUBI: isOsPackageDesired('docker-images') && !Boolean(flags['skip-docker-ubi']), createDockerContexts: !Boolean(flags['skip-docker-contexts']), + createDockerFIPS: isOsPackageDesired('docker-images') && !Boolean(flags['skip-docker-fips']), targetAllPlatforms: Boolean(flags['all-platforms']), eprRegistry: flags['epr-registry'], buildCanvasShareableRuntime: !Boolean(flags['skip-canvas-shareable-runtime']), diff --git a/src/dev/build/build_distributables.ts b/src/dev/build/build_distributables.ts index b324780e15672..ab9731e4ba112 100644 --- a/src/dev/build/build_distributables.ts +++ b/src/dev/build/build_distributables.ts @@ -34,6 +34,7 @@ export interface BuildOptions { createDockerCloud: boolean; createDockerServerless: boolean; createDockerContexts: boolean; + createDockerFIPS: boolean; versionQualifier: string | undefined; targetAllPlatforms: boolean; withExamplePlugins: boolean; @@ -163,6 +164,11 @@ export async function buildDistributables(log: ToolingLog, options: BuildOptions await run(Tasks.CreateDockerServerless); } + if (options.createDockerFIPS) { + // control w/ --docker-images or --skip-docker-fips or --skip-os-packages + await run(Tasks.CreateDockerFIPS); + } + if (options.createDockerContexts) { // control w/ --skip-docker-contexts await run(Tasks.CreateDockerContexts); diff --git a/src/dev/build/cli.ts b/src/dev/build/cli.ts index 09fd081136786..9a967e1cc85c9 100644 --- a/src/dev/build/cli.ts +++ b/src/dev/build/cli.ts @@ -45,6 +45,7 @@ if (showHelp) { --skip-canvas-shareable-runtime {dim Don't build the Canvas shareable runtime} --skip-docker-ubi {dim Don't build the docker ubi image} --skip-docker-ubuntu {dim Don't build the docker ubuntu image} + --skip-docker-fips {dim Don't build the docker fips image} --release {dim Produce a release-ready distributable} --version-qualifier {dim Suffix version with a qualifier} --skip-node-download {dim Reuse existing downloads of node.js} diff --git a/src/dev/build/tasks/os_packages/create_os_package_tasks.ts b/src/dev/build/tasks/os_packages/create_os_package_tasks.ts index 7cb9697364c75..e623dd86b9d6f 100644 --- a/src/dev/build/tasks/os_packages/create_os_package_tasks.ts +++ b/src/dev/build/tasks/os_packages/create_os_package_tasks.ts @@ -137,6 +137,20 @@ export const CreateDockerCloud: Task = { }, }; +export const CreateDockerFIPS: Task = { + description: 'Creating Docker FIPS image', + + async run(config, log, build) { + await runDockerGenerator(config, log, build, { + architecture: 'x64', + baseImage: 'ubi', + context: false, + image: true, + fips: true, + }); + }, +}; + export const CreateDockerContexts: Task = { description: 'Creating Docker build contexts', @@ -170,5 +184,11 @@ export const CreateDockerContexts: Task = { context: true, image: false, }); + await runDockerGenerator(config, log, build, { + baseImage: 'ubi', + context: true, + image: false, + fips: true, + }); }, }; diff --git a/src/dev/build/tasks/os_packages/docker_generator/bundle_dockerfiles.ts b/src/dev/build/tasks/os_packages/docker_generator/bundle_dockerfiles.ts index 7df5b21bd562a..0dac00084df0e 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/bundle_dockerfiles.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/bundle_dockerfiles.ts @@ -54,6 +54,12 @@ export async function bundleDockerFiles(config: Config, log: ToolingLog, scope: await write(resolve(dockerFilesBuildDir, template), output); } } + if (scope.fips) { + await copyAll( + resolve(scope.dockerBuildDir, 'openssl'), + resolve(dockerFilesBuildDir, 'openssl') + ); + } // Compress dockerfiles dir created inside // docker build dir as output it as a target diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/fips/openssl/nodejs.cnf b/src/dev/build/tasks/os_packages/docker_generator/resources/fips/openssl/nodejs.cnf new file mode 100644 index 0000000000000..bd8fece6674d7 --- /dev/null +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/fips/openssl/nodejs.cnf @@ -0,0 +1,28 @@ +########################################################################## +## ## +## This OpenSSL config is only loaded when running Kibana in FIPS mode. ## +## ## +## See: ## +## https://github.com/openssl/openssl/blob/openssl-3.0/README-FIPS.md ## +## https://www.openssl.org/docs/man3.0/man7/fips_module.html ## +## ## +########################################################################## + +nodejs_conf = nodejs_init +.include /usr/local/ssl/fipsmodule.cnf + +[nodejs_init] +providers = provider_sect +alg_section = algorithm_sect + +[provider_sect] +default = default_sect +# The fips section name should match the section name inside the +# included fipsmodule.cnf. +fips = fips_sect + +[default_sect] +activate = 1 + +[algorithm_sect] +default_properties = fips=yes \ No newline at end of file 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 16c48ad492187..cf2ddd34913b8 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/run.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/run.ts @@ -36,6 +36,7 @@ export async function runDockerGenerator( cloud?: boolean; serverless?: boolean; dockerBuildDate?: string; + fips?: boolean; } ) { let baseImageName = ''; @@ -47,6 +48,7 @@ export async function runDockerGenerator( if (flags.ironbank) imageFlavor += '-ironbank'; if (flags.cloud) imageFlavor += '-cloud'; if (flags.serverless) imageFlavor += '-serverless'; + if (flags.fips) imageFlavor += '-fips'; // General docker var config const license = 'Elastic License'; @@ -111,6 +113,7 @@ export async function runDockerGenerator( architecture: flags.architecture, revision: config.getBuildSha(), publicArtifactSubdomain, + fips: flags.fips, }; type HostArchitectureToDocker = Record; @@ -148,6 +151,14 @@ export async function runDockerGenerator( dockerBuildDir ); + // Copy fips related resources + if (flags.fips) { + await copyAll( + config.resolveFromRepo('src/dev/build/tasks/os_packages/docker_generator/resources/fips'), + dockerBuildDir + ); + } + // Build docker image into the target folder // In order to do this we just call the file we // created from the templates/build_docker_sh.template.js diff --git a/src/dev/build/tasks/os_packages/docker_generator/template_context.ts b/src/dev/build/tasks/os_packages/docker_generator/template_context.ts index edd0aed87e281..7734c347edfaa 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/template_context.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/template_context.ts @@ -33,4 +33,5 @@ export interface TemplateContext { ironbank?: boolean; revision: string; architecture?: string; + fips?: boolean; } diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile b/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile index 25f4345ffbcc3..b02efe3f5b5b3 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile @@ -61,7 +61,7 @@ EXPOSE 5601 RUN for iter in {1..10}; do \ {{packageManager}} update --setopt=tsflags=nodocs -y && \ {{packageManager}} install --setopt=tsflags=nodocs -y \ - fontconfig freetype shadow-utils nss findutils && \ + fontconfig freetype shadow-utils nss findutils {{#fips}}perl make gcc tar {{/fips}}&& \ {{packageManager}} clean all && exit_code=0 && break || exit_code=$? && echo "{{packageManager}} error: retry $iter in 10s" && \ sleep 10; \ done; \ @@ -112,6 +112,37 @@ COPY --from=builder --chown=1000:0 /usr/share/kibana /usr/share/kibana COPY --from=builder --chown=0:0 /opt /opt {{/cloud}} WORKDIR /usr/share/kibana +{{#fips}} + +# OpenSSL requires specific versions that are FIPS certified. Further, the FIPS modules +# need to be compiled on the machine to pass its own self validation on startup. +# +# See: +# https://github.com/openssl/openssl/blob/openssl-3.0/README-FIPS.md +# https://www.openssl.org/docs/man3.0/man7/fips_module.html + +# Ideally we would handle this in the builder step, but make is installing over the OS version +# of OpenSSL and requires linking of many submodules. +RUN set -e ; \ + curl --retry 8 -S -L -O https://www.openssl.org/source/openssl-3.0.8.tar.gz ; \ + curl --retry 8 -S -L -O https://www.openssl.org/source/openssl-3.0.8.tar.gz.sha256 ; \ + echo "$(cat openssl-3.0.8.tar.gz.sha256) openssl-3.0.8.tar.gz" | sha256sum -c ; \ + tar -zxf openssl-3.0.8.tar.gz ; \ + rm -rf openssl-3.0.8.tar* ; \ + cd /usr/share/kibana/openssl-3.0.8 ; \ + ./Configure enable-fips ; \ + make -j $(nproc) ; \ + make install ; \ + ldconfig /usr/local/lib64/ ; \ + chown -R 1000:0 /usr/share/kibana/openssl-3.0.8 + +# Enable FIPS for Kibana only. In the future we can override OS wide with ENV OPENSSL_CONF +RUN echo -e '\n--enable-fips' >> config/node.options +RUN echo '--openssl-config=/usr/share/kibana/openssl-3.0.8/nodejs.cnf' >> config/node.options +COPY --chown=1000:0 openssl/nodejs.cnf /usr/share/kibana/openssl-3.0.8/nodejs.cnf +ENV OPENSSL_MODULES=/usr/local/lib64/ossl-modules + +{{/fips}} RUN ln -s /usr/share/kibana /opt/kibana {{! Please notify @elastic/kibana-security if you want to remove or change this environment variable. }} @@ -127,7 +158,7 @@ COPY --chown=1000:0 config/serverless.es.yml /usr/share/kibana/config/serverless COPY --chown=1000:0 config/serverless.oblt.yml /usr/share/kibana/config/serverless.oblt.yml COPY --chown=1000:0 config/serverless.security.yml /usr/share/kibana/config/serverless.security.yml # Supportability enhancement: enable capturing heap snapshots. See https://nodejs.org/api/cli.html#--heapsnapshot-signalsignal -RUN echo '\n--heapsnapshot-signal=SIGUSR2' >> config/node.options +RUN echo -e '\n--heapsnapshot-signal=SIGUSR2' >> config/node.options RUN echo '--diagnostic-dir=./data' >> config/node.options {{/serverless}} {{^opensslLegacyProvider}} diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts b/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts index 57fc4d93a760a..dd35323808d51 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts @@ -19,7 +19,7 @@ function generator(options: TemplateContext) { packageManager: options.baseImage === 'ubi' ? 'microdnf' : 'apt-get', ubi: options.baseImage === 'ubi', ubuntu: options.baseImage === 'ubuntu', - opensslLegacyProvider: !(options.cloud || options.serverless), + opensslLegacyProvider: !(options.cloud || options.serverless || options.fips), ...options, }); } diff --git a/src/plugins/charts/common/constants.ts b/src/plugins/charts/common/constants.ts index 6d895dcba26ed..050157dd6cec7 100644 --- a/src/plugins/charts/common/constants.ts +++ b/src/plugins/charts/common/constants.ts @@ -13,7 +13,7 @@ export const paletteIds = [ 'custom', 'status', 'temperature', - 'complimentary', + 'complementary', 'negative', 'positive', 'cool', diff --git a/src/plugins/charts/public/services/palettes/palettes.tsx b/src/plugins/charts/public/services/palettes/palettes.tsx index 6cbbb548c7fed..4df7e2bea16a9 100644 --- a/src/plugins/charts/public/services/palettes/palettes.tsx +++ b/src/plugins/charts/public/services/palettes/palettes.tsx @@ -273,11 +273,11 @@ export const buildPalettes: ( title: i18n.translate('charts.palettes.temperatureLabel', { defaultMessage: 'Temperature' }), ...buildGradient('temperature', euiPaletteForTemperature), }, - complimentary: { - title: i18n.translate('charts.palettes.complimentaryLabel', { - defaultMessage: 'Complimentary', + complementary: { + title: i18n.translate('charts.palettes.complementaryLabel', { + defaultMessage: 'Complementary', }), - ...buildGradient('complimentary', euiPaletteComplementary), + ...buildGradient('complementary', euiPaletteComplementary), }, negative: { title: i18n.translate('charts.palettes.negativeLabel', { defaultMessage: 'Negative' }), diff --git a/src/plugins/charts/public/services/palettes/service.ts b/src/plugins/charts/public/services/palettes/service.ts index 0608c897f6306..7afa85ab58174 100644 --- a/src/plugins/charts/public/services/palettes/service.ts +++ b/src/plugins/charts/public/services/palettes/service.ts @@ -7,6 +7,7 @@ */ import type { PaletteRegistry, PaletteDefinition } from '@kbn/coloring'; +import { getActivePaletteName } from '@kbn/coloring'; import type { ExpressionsSetup } from '@kbn/expressions-plugin/public'; import type { ChartsPluginSetup } from '../..'; import type { LegacyColorsService } from '../legacy_colors'; @@ -29,7 +30,8 @@ export class PaletteService { } return { get: (name: string) => { - return this.palettes![name]; + const paletteName = getActivePaletteName(name); + return this.palettes![paletteName]; }, getAll: () => { return Object.values(this.palettes!); diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/add_panel_action_menu_items.test.ts b/src/plugins/dashboard/public/dashboard_app/top_nav/add_panel_action_menu_items.test.ts index 024a7518aba0c..aa8474a0879cc 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/add_panel_action_menu_items.test.ts +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/add_panel_action_menu_items.test.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { getMockPresentationContainer } from '@kbn/presentation-containers/mocks'; import { getAddPanelActionMenuItems } from './add_panel_action_menu_items'; describe('getAddPanelActionMenuItems', () => { @@ -21,7 +22,11 @@ describe('getAddPanelActionMenuItems', () => { execute: jest.fn(), }, ]; - const items = getAddPanelActionMenuItems(registeredActions, jest.fn(), jest.fn(), jest.fn()); + const items = getAddPanelActionMenuItems( + getMockPresentationContainer(), + registeredActions, + jest.fn() + ); expect(items).toStrictEqual([ { 'data-test-subj': 'create-action-Action name', @@ -34,7 +39,7 @@ describe('getAddPanelActionMenuItems', () => { }); it('returns empty array if no actions have been registered', async () => { - const items = getAddPanelActionMenuItems([], jest.fn(), jest.fn(), jest.fn()); + const items = getAddPanelActionMenuItems(getMockPresentationContainer(), [], jest.fn()); expect(items).toStrictEqual([]); }); }); diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/add_panel_action_menu_items.ts b/src/plugins/dashboard/public/dashboard_app/top_nav/add_panel_action_menu_items.ts index 3df8343887982..a6435579ff286 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/add_panel_action_menu_items.ts +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/add_panel_action_menu_items.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ import type { ActionExecutionContext, Action } from '@kbn/ui-actions-plugin/public'; -import type { EmbeddableFactory } from '@kbn/embeddable-plugin/public'; +import { PresentationContainer } from '@kbn/presentation-containers'; import { addPanelMenuTrigger } from '../../triggers'; const onAddPanelActionClick = @@ -27,16 +27,14 @@ const onAddPanelActionClick = }; export const getAddPanelActionMenuItems = ( + api: PresentationContainer, actions: Array> | undefined, - createNewEmbeddable: (embeddableFactory: EmbeddableFactory) => void, - deleteEmbeddable: (embeddableId: string) => void, closePopover: () => void ) => { return ( actions?.map((item) => { const context = { - createNewEmbeddable, - deleteEmbeddable, + embeddable: api, trigger: addPanelMenuTrigger, }; const actionName = item.getDisplayName(context); diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx index e39e227da0643..579d6d17d3a94 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx @@ -11,9 +11,7 @@ import { METRIC_TYPE } from '@kbn/analytics'; import { useEuiTheme } from '@elastic/eui'; import { AddFromLibraryButton, Toolbar, ToolbarButton } from '@kbn/shared-ux-button-toolbar'; -import { EmbeddableFactory, EmbeddableInput } from '@kbn/embeddable-plugin/public'; import { BaseVisType, VisTypeAlias } from '@kbn/visualizations-plugin/public'; -import { isExplicitInputWithAttributes } from '@kbn/embeddable-plugin/public'; import { getCreateVisualizationButtonTitle } from '../_dashboard_app_strings'; import { EditorMenu } from './editor_menu'; @@ -21,13 +19,11 @@ import { useDashboardAPI } from '../dashboard_app'; import { pluginServices } from '../../services/plugin_services'; import { ControlsToolbarButton } from './controls_toolbar_button'; import { DASHBOARD_UI_METRIC_ID } from '../../dashboard_constants'; -import { dashboardReplacePanelActionStrings } from '../../dashboard_actions/_dashboard_actions_strings'; export function DashboardEditingToolbar({ isDisabled }: { isDisabled?: boolean }) { const { usageCollection, data: { search }, - notifications: { toasts }, embeddable: { getStateTransfer }, visualizations: { getAliases: getVisTypeAliases }, } = pluginServices.getServices(); @@ -85,74 +81,9 @@ export function DashboardEditingToolbar({ isDisabled }: { isDisabled?: boolean } * initialInput: Optional, use it in case you want to pass your own input to the factory * dismissNotification: Optional, if not passed a toast will appear in the dashboard */ - const createNewEmbeddable = useCallback( - async ( - embeddableFactory: EmbeddableFactory, - initialInput?: Partial, - dismissNotification?: boolean - ) => { - if (trackUiMetric) { - trackUiMetric(METRIC_TYPE.CLICK, embeddableFactory.type); - } - - let explicitInput: Partial; - let attributes: unknown; - try { - if (initialInput) { - explicitInput = initialInput; - } else { - const explicitInputReturn = await embeddableFactory.getExplicitInput( - undefined, - dashboard - ); - if (isExplicitInputWithAttributes(explicitInputReturn)) { - explicitInput = explicitInputReturn.newInput; - attributes = explicitInputReturn.attributes; - } else { - explicitInput = explicitInputReturn; - } - } - } catch (e) { - // error likely means user canceled embeddable creation - return; - } - - const newEmbeddable = await dashboard.addNewEmbeddable( - embeddableFactory.type, - explicitInput, - attributes - ); - - if (newEmbeddable) { - dashboard.setScrollToPanelId(newEmbeddable.id); - dashboard.setHighlightPanelId(newEmbeddable.id); - - if (!dismissNotification) { - toasts.addSuccess({ - title: dashboardReplacePanelActionStrings.getSuccessMessage(newEmbeddable.getTitle()), - 'data-test-subj': 'addEmbeddableToDashboardSuccess', - }); - } - } - return newEmbeddable; - }, - [trackUiMetric, dashboard, toasts] - ); - - const deleteEmbeddable = useCallback( - (embeddableId: string) => { - dashboard.removeEmbeddable(embeddableId); - }, - [dashboard] - ); const extraButtons = [ - , + , dashboard.addFromLibrary()} size="s" diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx index 050b3ed818877..7df2dec31ae90 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx @@ -17,25 +17,15 @@ import { useEuiTheme, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { ToolbarPopover } from '@kbn/shared-ux-button-toolbar'; import type { Action } from '@kbn/ui-actions-plugin/public'; +import { ToolbarPopover } from '@kbn/shared-ux-button-toolbar'; +import { PresentationContainer } from '@kbn/presentation-containers'; import { type BaseVisType, VisGroups, type VisTypeAlias } from '@kbn/visualizations-plugin/public'; import type { EmbeddableFactory } from '@kbn/embeddable-plugin/public'; import { pluginServices } from '../../services/plugin_services'; import { DASHBOARD_APP_ID } from '../../dashboard_constants'; import { ADD_PANEL_TRIGGER } from '../../triggers'; import { getAddPanelActionMenuItems } from './add_panel_action_menu_items'; - -interface Props { - isDisabled?: boolean; - /** Handler for creating new visualization of a specified type */ - createNewVisType: (visType: BaseVisType | VisTypeAlias) => () => void; - /** Handler for creating a new embeddable of a specified type */ - createNewEmbeddable: (embeddableFactory: EmbeddableFactory) => void; - /** Handler for deleting an embeddable */ - deleteEmbeddable: (embeddableId: string) => void; -} - interface FactoryGroup { id: string; appName: string; @@ -51,10 +41,14 @@ interface UnwrappedEmbeddableFactory { export const EditorMenu = ({ createNewVisType, - createNewEmbeddable, - deleteEmbeddable, isDisabled, -}: Props) => { + api, +}: { + api: PresentationContainer; + isDisabled?: boolean; + /** Handler for creating new visualization of a specified type */ + createNewVisType: (visType: BaseVisType | VisTypeAlias) => () => void; +}) => { const isMounted = useRef(false); const { embeddable, @@ -148,16 +142,15 @@ export const EditorMenu = ({ // Retrieve ADD_PANEL_TRIGGER actions useEffect(() => { async function loadPanelActions() { - const registeredActions = await uiActions?.getTriggerCompatibleActions?.( - ADD_PANEL_TRIGGER, - {} - ); + const registeredActions = await uiActions?.getTriggerCompatibleActions?.(ADD_PANEL_TRIGGER, { + embeddable: api, + }); if (isMounted.current) { setAddPanelActions(registeredActions); } } loadPanelActions(); - }, [uiActions]); + }, [uiActions, api]); factories.forEach(({ factory }) => { const { grouping } = factory; @@ -249,7 +242,7 @@ export const EditorMenu = ({ toolTipContent, onClick: async () => { closePopover(); - createNewEmbeddable(factory); + api.addNewPanel({ panelType: factory.type }, true); }, 'data-test-subj': `createNew-${factory.type}`, }; @@ -274,12 +267,7 @@ export const EditorMenu = ({ })), ...promotedVisTypes.map(getVisTypeMenuItem), - ...getAddPanelActionMenuItems( - addPanelActions, - createNewEmbeddable, - deleteEmbeddable, - closePopover - ), + ...getAddPanelActionMenuItems(api, addPanelActions, closePopover), ]; if (aggsBasedVisTypes.length > 0) { initialPanelItems.push({ diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/run_save_functions.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/api/run_save_functions.tsx index 66c52f14a0002..75fd959c8e571 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/api/run_save_functions.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/api/run_save_functions.tsx @@ -14,7 +14,9 @@ import { showSaveModal } from '@kbn/saved-objects-plugin/public'; import { cloneDeep } from 'lodash'; import React from 'react'; import { batch } from 'react-redux'; -import { DashboardContainerInput } from '../../../../common'; + +import { EmbeddableInput, isReferenceOrValueEmbeddable } from '@kbn/embeddable-plugin/public'; +import { DashboardContainerInput, DashboardPanelMap } from '../../../../common'; import { DASHBOARD_CONTENT_ID, SAVED_OBJECT_POST_TIME } from '../../../dashboard_constants'; import { SaveDashboardReturn, @@ -185,7 +187,8 @@ export async function runQuickSave(this: DashboardContainer) { if (managed) return; const nextPanels = await serializeAllPanelState(this); - let stateToSave: SavedDashboardInput = { ...currentState, panels: nextPanels }; + const dashboardStateToSave: DashboardContainerInput = { ...currentState, panels: nextPanels }; + let stateToSave: SavedDashboardInput = dashboardStateToSave; let persistableControlGroupInput: PersistableControlGroupInput | undefined; if (this.controlGroup) { persistableControlGroupInput = this.controlGroup.getPersistableInput(); @@ -198,7 +201,7 @@ export async function runQuickSave(this: DashboardContainer) { saveOptions: {}, }); - this.dispatch.setLastSavedInput(currentState); + this.dispatch.setLastSavedInput(dashboardStateToSave); this.lastSavedState.next(); if (this.controlGroup && persistableControlGroupInput) { this.controlGroup.dispatch.setLastSavedInput(persistableControlGroupInput); @@ -241,12 +244,38 @@ export async function runClone(this: DashboardContainer) { }; } + const isManaged = this.getState().componentState.managed; + const newPanels = await (async () => { + if (!isManaged) return currentState.panels; + + // this is a managed dashboard - unlink all by reference embeddables on clone + const unlinkedPanels: DashboardPanelMap = {}; + for (const [panelId, panel] of Object.entries(currentState.panels)) { + const child = this.getChild(panelId); + if ( + child && + isReferenceOrValueEmbeddable(child) && + child.inputIsRefType(child.getInput() as EmbeddableInput) + ) { + const valueTypeInput = await child.getInputAsValueType(); + unlinkedPanels[panelId] = { + ...panel, + explicitInput: valueTypeInput, + }; + continue; + } + unlinkedPanels[panelId] = panel; + } + return unlinkedPanels; + })(); + const saveResult = await saveDashboardState({ saveOptions: { saveAsCopy: true, }, currentState: { ...stateToSave, + panels: newPanels, title: newTitle, }, }); diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx index 600ee15da2221..e58e8597500dd 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { v4 } from 'uuid'; import { omit } from 'lodash'; import React, { createContext, useContext } from 'react'; import ReactDOM from 'react-dom'; @@ -20,6 +21,8 @@ import type { DataView } from '@kbn/data-views-plugin/public'; import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; import { Container, + EmbeddableFactoryNotFoundError, + isExplicitInputWithAttributes, DefaultEmbeddableApi, PanelNotFoundError, ReactEmbeddableParentContext, @@ -30,8 +33,9 @@ import { type EmbeddableOutput, type IEmbeddable, } from '@kbn/embeddable-plugin/public'; -import type { Filter, Query, TimeRange } from '@kbn/es-query'; +import { METRIC_TYPE } from '@kbn/analytics'; import { I18nProvider } from '@kbn/i18n-react'; +import type { Filter, Query, TimeRange } from '@kbn/es-query'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { PanelPackage } from '@kbn/presentation-containers'; import { ReduxEmbeddableTools, ReduxToolsPackage } from '@kbn/presentation-util-plugin/public'; @@ -40,7 +44,13 @@ import { ExitFullScreenButtonKibanaProvider } from '@kbn/shared-ux-button-exit-f import { DashboardLocatorParams, DASHBOARD_CONTAINER_TYPE } from '../..'; import { DashboardContainerInput, DashboardPanelState } from '../../../common'; -import { DASHBOARD_APP_ID, DASHBOARD_LOADED_EVENT } from '../../dashboard_constants'; +import { + DASHBOARD_APP_ID, + DASHBOARD_LOADED_EVENT, + DASHBOARD_UI_METRIC_ID, + DEFAULT_PANEL_HEIGHT, + DEFAULT_PANEL_WIDTH, +} from '../../dashboard_constants'; import { DashboardAnalyticsService } from '../../services/analytics/types'; import { DashboardCapabilitiesService } from '../../services/dashboard_capabilities/types'; import { pluginServices } from '../../services/plugin_services'; @@ -70,6 +80,8 @@ import { dashboardTypeDisplayLowercase, dashboardTypeDisplayName, } from './dashboard_container_factory'; +import { dashboardReplacePanelActionStrings } from '../../dashboard_actions/_dashboard_actions_strings'; +import { panelPlacementStrategies } from '../component/panel_placement/place_new_panel_strategies'; export interface InheritedChildInput { filters: Filter[]; @@ -142,6 +154,9 @@ export class DashboardContainer private chrome; private customBranding; + private trackPanelAddMetric: + | ((type: string, eventNames: string | string[], count?: number | undefined) => void) + | undefined; // new embeddable framework public reactEmbeddableChildren: BehaviorSubject<{ [key: string]: DefaultEmbeddableApi }> = new BehaviorSubject<{ [key: string]: DefaultEmbeddableApi }>({}); @@ -156,9 +171,9 @@ export class DashboardContainer initialComponentState?: DashboardPublicState ) { const { + usageCollection, embeddable: { getEmbeddableFactory }, } = pluginServices.getServices(); - super( { ...initialInput, @@ -168,6 +183,11 @@ export class DashboardContainer parent ); + this.trackPanelAddMetric = usageCollection.reportUiCounter?.bind( + usageCollection, + DASHBOARD_UI_METRIC_ID + ); + ({ analytics: this.analyticsService, settings: { @@ -396,6 +416,88 @@ export class DashboardContainer return newId; } + public async addNewPanel( + panelPackage: PanelPackage, + displaySuccessMessage?: boolean + ) { + const { + notifications: { toasts }, + embeddable: { getEmbeddableFactory }, + } = pluginServices.getServices(); + + const onSuccess = (id?: string, title?: string) => { + if (!displaySuccessMessage) return; + toasts.addSuccess({ + title: dashboardReplacePanelActionStrings.getSuccessMessage(title), + 'data-test-subj': 'addEmbeddableToDashboardSuccess', + }); + this.setScrollToPanelId(id); + this.setHighlightPanelId(id); + }; + + if (this.trackPanelAddMetric) { + this.trackPanelAddMetric(METRIC_TYPE.CLICK, panelPackage.panelType); + } + if (reactEmbeddableRegistryHasKey(panelPackage.panelType)) { + const newId = v4(); + const { newPanelPlacement, otherPanels } = panelPlacementStrategies.findTopLeftMostOpenSpace({ + currentPanels: this.getInput().panels, + height: DEFAULT_PANEL_HEIGHT, + width: DEFAULT_PANEL_WIDTH, + }); + const newPanel: DashboardPanelState = { + type: panelPackage.panelType, + gridData: { + ...newPanelPlacement, + i: newId, + }, + explicitInput: { + ...panelPackage.initialState, + id: newId, + }, + }; + this.updateInput({ panels: { ...otherPanels, [newId]: newPanel } }); + onSuccess(newId, newPanel.explicitInput.title); + return; + } + + const embeddableFactory = getEmbeddableFactory(panelPackage.panelType); + if (!embeddableFactory) { + throw new EmbeddableFactoryNotFoundError(panelPackage.panelType); + } + const initialInput = panelPackage.initialState as Partial; + + let explicitInput: Partial; + let attributes: unknown; + try { + if (initialInput) { + explicitInput = initialInput; + } else { + const explicitInputReturn = await embeddableFactory.getExplicitInput(undefined, this); + if (isExplicitInputWithAttributes(explicitInputReturn)) { + explicitInput = explicitInputReturn.newInput; + attributes = explicitInputReturn.attributes; + } else { + explicitInput = explicitInputReturn; + } + } + } catch (e) { + // error likely means user canceled embeddable creation + return; + } + + const newEmbeddable = await this.addNewEmbeddable( + embeddableFactory.type, + explicitInput, + attributes + ); + + if (newEmbeddable) { + onSuccess(newEmbeddable.id, newEmbeddable.getTitle()); + } + return newEmbeddable as ApiType; + } + public getDashboardPanelFromId = async (panelId: string) => { const panel = this.getInput().panels[panelId]; if (reactEmbeddableRegistryHasKey(panel.type)) { diff --git a/src/plugins/data/common/search/expressions/esql.ts b/src/plugins/data/common/search/expressions/esql.ts index 1a2c7695bbf3d..ce32d3626bcfc 100644 --- a/src/plugins/data/common/search/expressions/esql.ts +++ b/src/plugins/data/common/search/expressions/esql.ts @@ -194,7 +194,10 @@ export const getEsqlFn = ({ getStartDependencies }: EsqlFnArguments) => { return search< IKibanaSearchRequest, IKibanaSearchResponse - >({ params }, { abortSignal, strategy: ESQL_ASYNC_SEARCH_STRATEGY }).pipe( + >( + { params: { ...params, dropNullColumns: true } }, + { abortSignal, strategy: ESQL_ASYNC_SEARCH_STRATEGY } + ).pipe( catchError((error) => { if (!error.attributes) { error.message = `Unexpected error from Elasticsearch: ${error.message}`; diff --git a/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts b/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts index 174f9924f1cc7..bcf8a3fafcf16 100644 --- a/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts +++ b/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts @@ -13,7 +13,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { firstValueFrom, from } from 'rxjs'; import { getKbnServerError } from '@kbn/kibana-utils-plugin/server'; import { IAsyncSearchRequestParams } from '../..'; -import { getKbnSearchError, KbnSearchError } from '../../report_search_error'; +import { getKbnSearchError } from '../../report_search_error'; import type { ISearchStrategy, SearchStrategyDependencies } from '../../types'; import type { IAsyncSearchOptions, @@ -170,14 +170,11 @@ export const enhancedEsSearchStrategyProvider = ( */ search: (request, options: IAsyncSearchOptions, deps) => { logger.debug(`search ${JSON.stringify(request.params) || request.id}`); - if (request.indexType && request.indexType !== 'rollup') { - throw new KbnSearchError('Unknown indexType', 400); - } - if (request.indexType === undefined || !deps.rollupsEnabled) { - return asyncSearch(request, options, deps); - } else { + if (request.indexType === 'rollup' && deps.rollupsEnabled) { return from(rollupSearch(request, options, deps)); + } else { + return asyncSearch(request, options, deps); } }, /** diff --git a/src/plugins/data/server/search/strategies/esql_async_search/esql_async_search_strategy.test.ts b/src/plugins/data/server/search/strategies/esql_async_search/esql_async_search_strategy.test.ts index b8a16113d1280..bc7bf31224940 100644 --- a/src/plugins/data/server/search/strategies/esql_async_search/esql_async_search_strategy.test.ts +++ b/src/plugins/data/server/search/strategies/esql_async_search/esql_async_search_strategy.test.ts @@ -123,7 +123,7 @@ describe('ES|QL async search strategy', () => { it('sets transport options on POST requests', async () => { const transportOptions = { maxRetries: 1 }; mockApiCaller.mockResolvedValueOnce(mockAsyncResponse); - const params = {}; + const params = { query: 'from logs' }; const esSearch = esqlAsyncSearchStrategyProvider(mockSearchConfig, mockLogger); await firstValueFrom( @@ -139,6 +139,7 @@ describe('ES|QL async search strategy', () => { keep_alive: '60000ms', wait_for_completion_timeout: '100ms', keep_on_completion: false, + query: 'from logs', }, }), expect.objectContaining({ maxRetries: 1, meta: true, signal: undefined }) diff --git a/src/plugins/data/server/search/strategies/esql_async_search/esql_async_search_strategy.ts b/src/plugins/data/server/search/strategies/esql_async_search/esql_async_search_strategy.ts index f052e6749e726..8bde400db2b45 100644 --- a/src/plugins/data/server/search/strategies/esql_async_search/esql_async_search_strategy.ts +++ b/src/plugins/data/server/search/strategies/esql_async_search/esql_async_search_strategy.ts @@ -11,6 +11,7 @@ import { catchError, tap } from 'rxjs/operators'; import { getKbnServerError } from '@kbn/kibana-utils-plugin/server'; import { SqlQueryRequest } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { SqlGetAsyncResponse } from '@elastic/elasticsearch/lib/api/types'; +import type { ESQLSearchParams } from '@kbn/es-types'; import { getCommonDefaultAsyncSubmitParams, getCommonDefaultAsyncGetParams, @@ -22,12 +23,18 @@ import { IKibanaSearchRequest, IKibanaSearchResponse, pollSearch } from '../../. import { toAsyncKibanaSearchResponse } from './response_utils'; import { SearchConfigSchema } from '../../../../config'; +// `drop_null_columns` is going to change the response +// now we get `all_columns` and `columns` +// `columns` contain only columns with data +// `all_columns` contain everything +type ESQLQueryRequest = ESQLSearchParams & SqlQueryRequest['body']; + export const esqlAsyncSearchStrategyProvider = ( searchConfig: SearchConfigSchema, logger: Logger, useInternalUser: boolean = false ): ISearchStrategy< - IKibanaSearchRequest, + IKibanaSearchRequest, IKibanaSearchResponse > => { function cancelAsyncSearch(id: string, esClient: IScopedClusterClient) { @@ -46,12 +53,12 @@ export const esqlAsyncSearchStrategyProvider = ( } function asyncSearch( - { id, ...request }: IKibanaSearchRequest, + { id, ...request }: IKibanaSearchRequest, options: IAsyncSearchOptions, { esClient, uiSettingsClient }: SearchStrategyDependencies ) { const client = useInternalUser ? esClient.asInternalUser : esClient.asCurrentUser; - + const { dropNullColumns, ...requestParams } = request.params ?? {}; const search = async () => { const params = id ? { @@ -63,7 +70,7 @@ export const esqlAsyncSearchStrategyProvider = ( } : { ...(await getCommonDefaultAsyncSubmitParams(searchConfig, options)), - ...request.params, + ...requestParams, }; const { body, headers, meta } = id ? await client.transport.request( @@ -79,7 +86,7 @@ export const esqlAsyncSearchStrategyProvider = ( method: 'POST', path: `/_query/async`, body: params, - querystring: 'drop_null_columns', + querystring: dropNullColumns ? 'drop_null_columns' : '', }, { ...options.transport, signal: options.abortSignal, meta: true } ); diff --git a/src/plugins/data/server/search/strategies/esql_search/esql_search_strategy.ts b/src/plugins/data/server/search/strategies/esql_search/esql_search_strategy.ts index e9a6499b4aa1b..73e77e53555e2 100644 --- a/src/plugins/data/server/search/strategies/esql_search/esql_search_strategy.ts +++ b/src/plugins/data/server/search/strategies/esql_search/esql_search_strategy.ts @@ -32,12 +32,16 @@ export const esqlSearchStrategyProvider = ( const search = async () => { try { - const { terminateAfter, ...requestParams } = request.params ?? {}; + // `drop_null_columns` is going to change the response + // now we get `all_columns` and `columns` + // `columns` contain only columns with data + // `all_columns` contain everything + const { terminateAfter, dropNullColumns, ...requestParams } = request.params ?? {}; const { headers, body, meta } = await esClient.asCurrentUser.transport.request( { method: 'POST', path: `/_query`, - querystring: 'drop_null_columns', + querystring: dropNullColumns ? 'drop_null_columns' : '', body: { ...requestParams, }, diff --git a/src/plugins/data_views/common/data_views/__snapshots__/data_views.test.ts.snap b/src/plugins/data_views/common/data_views/__snapshots__/data_views.test.ts.snap index 53b77e28a416c..cb9bb0517f507 100644 --- a/src/plugins/data_views/common/data_views/__snapshots__/data_views.test.ts.snap +++ b/src/plugins/data_views/common/data_views/__snapshots__/data_views.test.ts.snap @@ -7,6 +7,7 @@ FldList [ "conflictDescriptions": undefined, "count": 5, "customLabel": "A Runtime Field", + "defaultFormatter": undefined, "esTypes": Array [ "keyword", ], diff --git a/src/plugins/data_views/common/data_views/abstract_data_views.ts b/src/plugins/data_views/common/data_views/abstract_data_views.ts index 15ec8342cfd5b..cc69ed7e7cedf 100644 --- a/src/plugins/data_views/common/data_views/abstract_data_views.ts +++ b/src/plugins/data_views/common/data_views/abstract_data_views.ts @@ -24,6 +24,7 @@ import type { RuntimeField, } from '../types'; import { removeFieldAttrs } from './utils'; +import { metaUnitsToFormatter } from './meta_units_to_formatter'; import type { DataViewAttributes, FieldAttrs, FieldAttrSet } from '..'; @@ -251,6 +252,11 @@ export abstract class AbstractDataView { return fieldFormat; } + const fmt = field.defaultFormatter ? metaUnitsToFormatter[field.defaultFormatter] : undefined; + if (fmt) { + return this.fieldFormats.getInstance(fmt.id, fmt.params); + } + return this.fieldFormats.getDefaultInstance( field.type as KBN_FIELD_TYPES, field.esTypes as ES_FIELD_TYPES[] diff --git a/src/plugins/data_views/common/data_views/meta_units_to_formatter.ts b/src/plugins/data_views/common/data_views/meta_units_to_formatter.ts new file mode 100644 index 0000000000000..2989bf5828c37 --- /dev/null +++ b/src/plugins/data_views/common/data_views/meta_units_to_formatter.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { FieldFormatParams } from '@kbn/field-formats-plugin/common'; + +const timeUnitToDurationFmt = (inputFormat = 'milliseconds') => { + return { + id: 'duration', + params: { + inputFormat, + outputFormat: 'humanizePrecise', + outputPrecision: 2, + includeSpaceWithSuffix: true, + useShortSuffix: true, + }, + }; +}; + +export const metaUnitsToFormatter: Record = { + percent: { id: 'percent' }, + byte: { id: 'bytes' }, + nanos: timeUnitToDurationFmt('nanoseconds'), + micros: timeUnitToDurationFmt('microseconds'), + ms: timeUnitToDurationFmt('milliseconds'), + s: timeUnitToDurationFmt('seconds'), + m: timeUnitToDurationFmt('minutes'), + h: timeUnitToDurationFmt('hours'), + d: timeUnitToDurationFmt('days'), +}; diff --git a/src/plugins/data_views/common/fields/data_view_field.ts b/src/plugins/data_views/common/fields/data_view_field.ts index 36cd78682aa97..52c304dbf27f1 100644 --- a/src/plugins/data_views/common/fields/data_view_field.ts +++ b/src/plugins/data_views/common/fields/data_view_field.ts @@ -69,6 +69,10 @@ export class DataViewField implements DataViewFieldBase { this.spec.count = count; } + public get defaultFormatter() { + return this.spec.defaultFormatter; + } + /** * Returns runtime field definition or undefined if field is not runtime field. */ @@ -370,6 +374,7 @@ export class DataViewField implements DataViewFieldBase { readFromDocValues: this.readFromDocValues, subType: this.subType, customLabel: this.customLabel, + defaultFormatter: this.defaultFormatter, }; } @@ -403,6 +408,7 @@ export class DataViewField implements DataViewFieldBase { timeSeriesMetric: this.spec.timeSeriesMetric, timeZone: this.spec.timeZone, fixedInterval: this.spec.fixedInterval, + defaultFormatter: this.defaultFormatter, }; // Filter undefined values from the spec diff --git a/src/plugins/data_views/common/types.ts b/src/plugins/data_views/common/types.ts index 2177f51621fec..caf1fc80dc5b6 100644 --- a/src/plugins/data_views/common/types.ts +++ b/src/plugins/data_views/common/types.ts @@ -462,6 +462,8 @@ export type FieldSpec = DataViewFieldBase & { * Name of parent field for composite runtime field subfields. */ parentName?: string; + + defaultFormatter?: string; }; export type DataViewFieldMap = Record; diff --git a/src/plugins/data_views/docs/openapi/bundled.json b/src/plugins/data_views/docs/openapi/bundled.json index 52912da70f307..3b435603f7d34 100644 --- a/src/plugins/data_views/docs/openapi/bundled.json +++ b/src/plugins/data_views/docs/openapi/bundled.json @@ -32,7 +32,7 @@ } ], "paths": { - "/api/data_views": { + "/s/{spaceId}/api/data_views": { "get": { "summary": "Retrieves a list of all data views.", "operationId": "getAllDataViews", @@ -40,6 +40,11 @@ "tags": [ "data views" ], + "parameters": [ + { + "$ref": "#/components/parameters/space_id" + } + ], "responses": { "200": { "description": "Indicates a successful call.", @@ -97,7 +102,7 @@ } } }, - "/api/data_views/data_view": { + "/s/{spaceId}/api/data_views/data_view": { "post": { "summary": "Creates a data view.", "operationId": "createDataView", @@ -108,6 +113,9 @@ "parameters": [ { "$ref": "#/components/parameters/kbn_xsrf" + }, + { + "$ref": "#/components/parameters/space_id" } ], "requestBody": { @@ -149,7 +157,7 @@ } } }, - "/api/data_views/data_view/{viewId}": { + "/s/{spaceId}/api/data_views/data_view/{viewId}": { "get": { "summary": "Retrieves a single data view by identifier.", "operationId": "getDataView", @@ -160,6 +168,9 @@ "parameters": [ { "$ref": "#/components/parameters/view_id" + }, + { + "$ref": "#/components/parameters/space_id" } ], "responses": { @@ -203,6 +214,9 @@ }, { "$ref": "#/components/parameters/view_id" + }, + { + "$ref": "#/components/parameters/space_id" } ], "responses": { @@ -234,6 +248,9 @@ }, { "$ref": "#/components/parameters/view_id" + }, + { + "$ref": "#/components/parameters/space_id" } ], "requestBody": { @@ -275,7 +292,129 @@ } } }, - "/api/data_views/data_view/{viewId}/fields": { + "/s/{spaceId}/api/data_views/default": { + "get": { + "summary": "Retrieves the default data view identifier.", + "operationId": "getDefaultDataView", + "description": "This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.\n", + "tags": [ + "data views" + ], + "parameters": [ + { + "$ref": "#/components/parameters/space_id" + } + ], + "responses": { + "200": { + "description": "Indicates a successful call.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data_view_id": { + "type": "string" + } + } + }, + "examples": { + "getDefaultDataViewResponse": { + "$ref": "#/components/examples/get_default_data_view_response" + } + } + } + } + }, + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/400_response" + } + } + } + } + } + }, + "post": { + "summary": "Sets the default data view identifier.", + "operationId": "setDefaultDatailView", + "description": "This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.\n", + "tags": [ + "data views" + ], + "parameters": [ + { + "$ref": "#/components/parameters/kbn_xsrf" + }, + { + "$ref": "#/components/parameters/space_id" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "data_view_id" + ], + "properties": { + "data_view_id": { + "type": [ + "string", + "null" + ], + "description": "The data view identifier. NOTE: The API does not validate whether it is a valid identifier. Use `null` to unset the default data view.\n" + }, + "force": { + "type": "boolean", + "description": "Update an existing default data view identifier.", + "default": false + } + } + }, + "examples": { + "setDefaultDataViewRequest": { + "$ref": "#/components/examples/set_default_data_view_request" + } + } + } + } + }, + "responses": { + "200": { + "description": "Indicates a successful call.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "acknowledged": { + "type": "boolean" + } + } + } + } + } + }, + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/400_response" + } + } + } + } + } + } + }, + "/s/{spaceId}/api/data_views/data_view/{viewId}/fields": { "post": { "summary": "Update fields presentation metadata such as count, customLabel and format.", "operationId": "updateFieldsMetadata", @@ -289,6 +428,9 @@ }, { "$ref": "#/components/parameters/view_id" + }, + { + "$ref": "#/components/parameters/space_id" } ], "requestBody": { @@ -344,7 +486,7 @@ } } }, - "/api/data_views/data_view/{viewId}/runtime_field": { + "/s/{spaceId}/api/data_views/data_view/{viewId}/runtime_field": { "post": { "summary": "Creates a runtime field.", "operationId": "createRuntimeField", @@ -358,6 +500,9 @@ }, { "$ref": "#/components/parameters/view_id" + }, + { + "$ref": "#/components/parameters/space_id" } ], "requestBody": { @@ -401,6 +546,9 @@ { "$ref": "#/components/parameters/kbn_xsrf" }, + { + "$ref": "#/components/parameters/space_id" + }, { "name": "viewId", "in": "path", @@ -480,7 +628,7 @@ } } }, - "/api/data_views/data_view/{viewId}/runtime_field/{fieldName}": { + "/s/{spaceId}/api/data_views/data_view/{viewId}/runtime_field/{fieldName}": { "get": { "summary": "Retrieves a runtime field.", "operationId": "getRuntimeField", @@ -494,6 +642,9 @@ }, { "$ref": "#/components/parameters/view_id" + }, + { + "$ref": "#/components/parameters/space_id" } ], "responses": { @@ -548,6 +699,9 @@ }, { "$ref": "#/components/parameters/view_id" + }, + { + "$ref": "#/components/parameters/space_id" } ], "responses": { @@ -579,6 +733,9 @@ }, { "$ref": "#/components/parameters/view_id" + }, + { + "$ref": "#/components/parameters/space_id" } ], "requestBody": { @@ -621,120 +778,6 @@ } } } - }, - "/api/data_views/default": { - "get": { - "summary": "Retrieves the default data view identifier.", - "operationId": "getDefaultDataView", - "description": "This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.\n", - "tags": [ - "data views" - ], - "responses": { - "200": { - "description": "Indicates a successful call.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "data_view_id": { - "type": "string" - } - } - }, - "examples": { - "getDefaultDataViewResponse": { - "$ref": "#/components/examples/get_default_data_view_response" - } - } - } - } - }, - "400": { - "description": "Bad request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/400_response" - } - } - } - } - } - }, - "post": { - "summary": "Sets the default data view identifier.", - "operationId": "setDefaultDatailView", - "description": "This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.\n", - "tags": [ - "data views" - ], - "parameters": [ - { - "$ref": "#/components/parameters/kbn_xsrf" - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "data_view_id" - ], - "properties": { - "data_view_id": { - "type": [ - "string", - "null" - ], - "description": "The data view identifier. NOTE: The API does not validate whether it is a valid identifier. Use `null` to unset the default data view.\n" - }, - "force": { - "type": "boolean", - "description": "Update an existing default data view identifier.", - "default": false - } - } - }, - "examples": { - "setDefaultDataViewRequest": { - "$ref": "#/components/examples/set_default_data_view_request" - } - } - } - } - }, - "responses": { - "200": { - "description": "Indicates a successful call.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "acknowledged": { - "type": "boolean" - } - } - } - } - } - }, - "400": { - "description": "Bad request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/400_response" - } - } - } - } - } - } } }, "components": { @@ -750,6 +793,49 @@ "description": "e.g. Authorization: ApiKey base64AccessApiKey" } }, + "parameters": { + "space_id": { + "in": "path", + "name": "spaceId", + "description": "An identifier for the space. If `/s/` and the identifier are omitted from the path, the default space is used.", + "required": true, + "schema": { + "type": "string", + "examples": [ + "default" + ] + } + }, + "kbn_xsrf": { + "schema": { + "type": "string" + }, + "in": "header", + "name": "kbn-xsrf", + "description": "Cross-site request forgery protection", + "required": true + }, + "view_id": { + "in": "path", + "name": "viewId", + "description": "An identifier for the data view.", + "required": true, + "schema": { + "type": "string", + "example": "ff959d40-b880-11e8-a6d9-e546fe2bba5f" + } + }, + "field_name": { + "in": "path", + "name": "fieldName", + "description": "The name of the runtime field.", + "required": true, + "schema": { + "type": "string", + "example": "hour_of_day" + } + } + }, "examples": { "get_data_views_response": { "summary": "The get all data views API returns a list of data views.", @@ -1969,6 +2055,19 @@ "refresh_fields": true } }, + "get_default_data_view_response": { + "summary": "The get default data view API returns the default data view identifier.", + "value": { + "data_view_id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f" + } + }, + "set_default_data_view_request": { + "summary": "Set the default data view identifier.", + "value": { + "data_view_id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "force": true + } + }, "update_field_metadata_request": { "summary": "Set popularity count for field foo.", "value": { @@ -2629,19 +2728,6 @@ } } } - }, - "get_default_data_view_response": { - "summary": "The get default data view API returns the default data view identifier.", - "value": { - "data_view_id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f" - } - }, - "set_default_data_view_request": { - "summary": "Set the default data view identifier.", - "value": { - "data_view_id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", - "force": true - } } }, "schemas": { @@ -2914,37 +3000,6 @@ } } } - }, - "parameters": { - "kbn_xsrf": { - "schema": { - "type": "string" - }, - "in": "header", - "name": "kbn-xsrf", - "description": "Cross-site request forgery protection", - "required": true - }, - "view_id": { - "in": "path", - "name": "viewId", - "description": "An identifier for the data view.", - "required": true, - "schema": { - "type": "string", - "example": "ff959d40-b880-11e8-a6d9-e546fe2bba5f" - } - }, - "field_name": { - "in": "path", - "name": "fieldName", - "description": "The name of the runtime field.", - "required": true, - "schema": { - "type": "string", - "example": "hour_of_day" - } - } } } } \ No newline at end of file diff --git a/src/plugins/data_views/docs/openapi/bundled.yaml b/src/plugins/data_views/docs/openapi/bundled.yaml index a0afe1e93a8ef..5f8ce9d8c18f7 100644 --- a/src/plugins/data_views/docs/openapi/bundled.yaml +++ b/src/plugins/data_views/docs/openapi/bundled.yaml @@ -17,7 +17,7 @@ tags: - name: data views description: Data view APIs enable you to manage data views, formerly known as Kibana index patterns. paths: - /api/data_views: + /s/{spaceId}/api/data_views: get: summary: Retrieves a list of all data views. operationId: getAllDataViews @@ -25,6 +25,8 @@ paths: This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. tags: - data views + parameters: + - $ref: '#/components/parameters/space_id' responses: '200': description: Indicates a successful call. @@ -59,7 +61,7 @@ paths: application/json: schema: $ref: '#/components/schemas/400_response' - /api/data_views/data_view: + /s/{spaceId}/api/data_views/data_view: post: summary: Creates a data view. operationId: createDataView @@ -69,6 +71,7 @@ paths: - data views parameters: - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/space_id' requestBody: required: true content: @@ -91,7 +94,7 @@ paths: application/json: schema: $ref: '#/components/schemas/400_response' - /api/data_views/data_view/{viewId}: + /s/{spaceId}/api/data_views/data_view/{viewId}: get: summary: Retrieves a single data view by identifier. operationId: getDataView @@ -101,6 +104,7 @@ paths: - data views parameters: - $ref: '#/components/parameters/view_id' + - $ref: '#/components/parameters/space_id' responses: '200': description: Indicates a successful call. @@ -127,6 +131,7 @@ paths: parameters: - $ref: '#/components/parameters/kbn_xsrf' - $ref: '#/components/parameters/view_id' + - $ref: '#/components/parameters/space_id' responses: '204': description: Indicates a successful call. @@ -146,6 +151,7 @@ paths: parameters: - $ref: '#/components/parameters/kbn_xsrf' - $ref: '#/components/parameters/view_id' + - $ref: '#/components/parameters/space_id' requestBody: required: true content: @@ -168,7 +174,84 @@ paths: application/json: schema: $ref: '#/components/schemas/400_response' - /api/data_views/data_view/{viewId}/fields: + /s/{spaceId}/api/data_views/default: + get: + summary: Retrieves the default data view identifier. + operationId: getDefaultDataView + description: | + This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. + tags: + - data views + parameters: + - $ref: '#/components/parameters/space_id' + responses: + '200': + description: Indicates a successful call. + content: + application/json: + schema: + type: object + properties: + data_view_id: + type: string + examples: + getDefaultDataViewResponse: + $ref: '#/components/examples/get_default_data_view_response' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/400_response' + post: + summary: Sets the default data view identifier. + operationId: setDefaultDatailView + description: | + This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. + tags: + - data views + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/space_id' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - data_view_id + properties: + data_view_id: + type: + - string + - 'null' + description: | + The data view identifier. NOTE: The API does not validate whether it is a valid identifier. Use `null` to unset the default data view. + force: + type: boolean + description: Update an existing default data view identifier. + default: false + examples: + setDefaultDataViewRequest: + $ref: '#/components/examples/set_default_data_view_request' + responses: + '200': + description: Indicates a successful call. + content: + application/json: + schema: + type: object + properties: + acknowledged: + type: boolean + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/400_response' + /s/{spaceId}/api/data_views/data_view/{viewId}/fields: post: summary: Update fields presentation metadata such as count, customLabel and format. operationId: updateFieldsMetadata @@ -179,6 +262,7 @@ paths: parameters: - $ref: '#/components/parameters/kbn_xsrf' - $ref: '#/components/parameters/view_id' + - $ref: '#/components/parameters/space_id' requestBody: required: true content: @@ -210,7 +294,7 @@ paths: application/json: schema: $ref: '#/components/schemas/400_response' - /api/data_views/data_view/{viewId}/runtime_field: + /s/{spaceId}/api/data_views/data_view/{viewId}/runtime_field: post: summary: Creates a runtime field. operationId: createRuntimeField @@ -221,6 +305,7 @@ paths: parameters: - $ref: '#/components/parameters/kbn_xsrf' - $ref: '#/components/parameters/view_id' + - $ref: '#/components/parameters/space_id' requestBody: required: true content: @@ -251,6 +336,7 @@ paths: - data views parameters: - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/space_id' - name: viewId in: path description: | @@ -302,7 +388,7 @@ paths: application/json: schema: $ref: '#/components/schemas/400_response' - /api/data_views/data_view/{viewId}/runtime_field/{fieldName}: + /s/{spaceId}/api/data_views/data_view/{viewId}/runtime_field/{fieldName}: get: summary: Retrieves a runtime field. operationId: getRuntimeField @@ -313,6 +399,7 @@ paths: parameters: - $ref: '#/components/parameters/field_name' - $ref: '#/components/parameters/view_id' + - $ref: '#/components/parameters/space_id' responses: '200': description: Indicates a successful call. @@ -346,6 +433,7 @@ paths: parameters: - $ref: '#/components/parameters/field_name' - $ref: '#/components/parameters/view_id' + - $ref: '#/components/parameters/space_id' responses: '200': description: Indicates a successful call. @@ -365,6 +453,7 @@ paths: parameters: - $ref: '#/components/parameters/field_name' - $ref: '#/components/parameters/view_id' + - $ref: '#/components/parameters/space_id' requestBody: required: true content: @@ -395,80 +484,6 @@ paths: application/json: schema: $ref: '#/components/schemas/400_response' - /api/data_views/default: - get: - summary: Retrieves the default data view identifier. - operationId: getDefaultDataView - description: | - This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. - tags: - - data views - responses: - '200': - description: Indicates a successful call. - content: - application/json: - schema: - type: object - properties: - data_view_id: - type: string - examples: - getDefaultDataViewResponse: - $ref: '#/components/examples/get_default_data_view_response' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/400_response' - post: - summary: Sets the default data view identifier. - operationId: setDefaultDatailView - description: | - This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. - tags: - - data views - parameters: - - $ref: '#/components/parameters/kbn_xsrf' - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - data_view_id - properties: - data_view_id: - type: - - string - - 'null' - description: | - The data view identifier. NOTE: The API does not validate whether it is a valid identifier. Use `null` to unset the default data view. - force: - type: boolean - description: Update an existing default data view identifier. - default: false - examples: - setDefaultDataViewRequest: - $ref: '#/components/examples/set_default_data_view_request' - responses: - '200': - description: Indicates a successful call. - content: - application/json: - schema: - type: object - properties: - acknowledged: - type: boolean - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/400_response' components: securitySchemes: basicAuth: @@ -479,6 +494,39 @@ components: in: header name: Authorization description: 'e.g. Authorization: ApiKey base64AccessApiKey' + parameters: + space_id: + in: path + name: spaceId + description: An identifier for the space. If `/s/` and the identifier are omitted from the path, the default space is used. + required: true + schema: + type: string + examples: + - default + kbn_xsrf: + schema: + type: string + in: header + name: kbn-xsrf + description: Cross-site request forgery protection + required: true + view_id: + in: path + name: viewId + description: An identifier for the data view. + required: true + schema: + type: string + example: ff959d40-b880-11e8-a6d9-e546fe2bba5f + field_name: + in: path + name: fieldName + description: The name of the runtime field. + required: true + schema: + type: string + example: hour_of_day examples: get_data_views_response: summary: The get all data views API returns a list of data views. @@ -1447,6 +1495,15 @@ components: allowNoIndex: false name: Kibana Sample Data eCommerce refresh_fields: true + get_default_data_view_response: + summary: The get default data view API returns the default data view identifier. + value: + data_view_id: ff959d40-b880-11e8-a6d9-e546fe2bba5f + set_default_data_view_request: + summary: Set the default data view identifier. + value: + data_view_id: ff959d40-b880-11e8-a6d9-e546fe2bba5f + force: true update_field_metadata_request: summary: Set popularity count for field foo. value: @@ -1973,15 +2030,6 @@ components: runtimeField: script: source: emit(doc["bar"].value) - get_default_data_view_response: - summary: The get default data view API returns the default data view identifier. - value: - data_view_id: ff959d40-b880-11e8-a6d9-e546fe2bba5f - set_default_data_view_request: - summary: Set the default data view identifier. - value: - data_view_id: ff959d40-b880-11e8-a6d9-e546fe2bba5f - force: true schemas: 400_response: title: Bad request @@ -2171,27 +2219,3 @@ components: type: boolean description: Reloads the data view fields after the data view is updated. default: false - parameters: - kbn_xsrf: - schema: - type: string - in: header - name: kbn-xsrf - description: Cross-site request forgery protection - required: true - view_id: - in: path - name: viewId - description: An identifier for the data view. - required: true - schema: - type: string - example: ff959d40-b880-11e8-a6d9-e546fe2bba5f - field_name: - in: path - name: fieldName - description: The name of the runtime field. - required: true - schema: - type: string - example: hour_of_day diff --git a/src/plugins/data_views/docs/openapi/components/parameters/space_id.yaml b/src/plugins/data_views/docs/openapi/components/parameters/space_id.yaml new file mode 100644 index 0000000000000..45787e844caec --- /dev/null +++ b/src/plugins/data_views/docs/openapi/components/parameters/space_id.yaml @@ -0,0 +1,8 @@ +in: path +name: spaceId +description: An identifier for the space. If `/s/` and the identifier are omitted from the path, the default space is used. +required: true +schema: + type: string + examples: + - default diff --git a/src/plugins/data_views/docs/openapi/entrypoint.yaml b/src/plugins/data_views/docs/openapi/entrypoint.yaml index 697fd554dae8c..86d76dbfa42d4 100644 --- a/src/plugins/data_views/docs/openapi/entrypoint.yaml +++ b/src/plugins/data_views/docs/openapi/entrypoint.yaml @@ -15,35 +15,35 @@ servers: - url: / paths: # Default space - '/api/data_views': - $ref: 'paths/api@data_views.yaml' - '/api/data_views/data_view': - $ref: 'paths/api@data_views@data_view.yaml' - '/api/data_views/data_view/{viewId}': - $ref: 'paths/api@data_views@data_view@{viewid}.yaml' - '/api/data_views/data_view/{viewId}/fields': - $ref: 'paths/api@data_views@data_view@{viewid}@fields.yaml' - '/api/data_views/data_view/{viewId}/runtime_field': - $ref: 'paths/api@data_views@data_view@{viewid}@runtime_field.yaml' - '/api/data_views/data_view/{viewId}/runtime_field/{fieldName}': - $ref: 'paths/api@data_views@data_view@{viewid}@runtime_field@{fieldname}.yaml' - '/api/data_views/default': - $ref: 'paths/api@data_views@default.yaml' + # '/api/data_views': + # $ref: 'paths/api@data_views.yaml' + # '/api/data_views/data_view': + # $ref: 'paths/api@data_views@data_view.yaml' + # '/api/data_views/data_view/{viewId}': + # $ref: 'paths/api@data_views@data_view@{viewid}.yaml' + # '/api/data_views/data_view/{viewId}/fields': + # $ref: 'paths/api@data_views@data_view@{viewid}@fields.yaml' + # '/api/data_views/data_view/{viewId}/runtime_field': + # $ref: 'paths/api@data_views@data_view@{viewid}@runtime_field.yaml' + # '/api/data_views/data_view/{viewId}/runtime_field/{fieldName}': + # $ref: 'paths/api@data_views@data_view@{viewid}@runtime_field@{fieldname}.yaml' + # '/api/data_views/default': + # $ref: 'paths/api@data_views@default.yaml' # Non-default space -# '/s/{spaceId}/api/data_views': -# $ref: 'paths/s@{spaceid}@api@data_views.yaml' -# '/s/{spaceId}/api/data_views/data_view': -# $ref: 'paths/s@{spaceid}@api@data_views@data_view.yaml' -# '/s/{spaceId}/api/data_views/data_view/{viewId}': -# $ref: 'paths/s@{spaceid}@api@data_views@data_view@{viewid}.yaml' -# '/s/{spaceId}/api/data_views/default': -# $ref: 'paths/s@{spaceid}@api@data_views@default.yaml' -# '/s/{spaceId}/api/data_views/data_view/{viewId}/fields': -# $ref: 'paths/s@{spaceid}@api@data_views@data_view@{viewid}@fields.yaml' -# '/s/{spaceId}/api/data_views/data_view/{viewId}/runtime_field': -# $ref: 'paths/s@{spaceid}@api@data_views@data_view@{viewid}@runtime_field.yaml' -# '/s/{spaceId}/api/data_views/data_view/{viewId}/runtime_field/{fieldName}': -# $ref: 'paths/s@{spaceid}@api@data_views@data_view@{viewid}@runtime_field@{fieldname}.yaml' + '/s/{spaceId}/api/data_views': + $ref: 'paths/s@{spaceid}@api@data_views.yaml' + '/s/{spaceId}/api/data_views/data_view': + $ref: 'paths/s@{spaceid}@api@data_views@data_view.yaml' + '/s/{spaceId}/api/data_views/data_view/{viewId}': + $ref: 'paths/s@{spaceid}@api@data_views@data_view@{viewid}.yaml' + '/s/{spaceId}/api/data_views/default': + $ref: 'paths/s@{spaceid}@api@data_views@default.yaml' + '/s/{spaceId}/api/data_views/data_view/{viewId}/fields': + $ref: 'paths/s@{spaceid}@api@data_views@data_view@{viewid}@fields.yaml' + '/s/{spaceId}/api/data_views/data_view/{viewId}/runtime_field': + $ref: 'paths/s@{spaceid}@api@data_views@data_view@{viewid}@runtime_field.yaml' + '/s/{spaceId}/api/data_views/data_view/{viewId}/runtime_field/{fieldName}': + $ref: 'paths/s@{spaceid}@api@data_views@data_view@{viewid}@runtime_field@{fieldname}.yaml' components: securitySchemes: basicAuth: diff --git a/src/plugins/data_views/docs/openapi/paths/api@data_views.yaml b/src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views.yaml similarity index 95% rename from src/plugins/data_views/docs/openapi/paths/api@data_views.yaml rename to src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views.yaml index e36f914e55e54..b51fd231e4332 100644 --- a/src/plugins/data_views/docs/openapi/paths/api@data_views.yaml +++ b/src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views.yaml @@ -5,6 +5,8 @@ get: This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. tags: - data views + parameters: + - $ref: '../components/parameters/space_id.yaml' responses: '200': description: Indicates a successful call. diff --git a/src/plugins/data_views/docs/openapi/paths/api@data_views@data_view.yaml b/src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views@data_view.yaml similarity index 95% rename from src/plugins/data_views/docs/openapi/paths/api@data_views@data_view.yaml rename to src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views@data_view.yaml index 32a7e0b5c42dc..230427f30187d 100644 --- a/src/plugins/data_views/docs/openapi/paths/api@data_views@data_view.yaml +++ b/src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views@data_view.yaml @@ -7,6 +7,7 @@ post: - data views parameters: - $ref: '../components/headers/kbn_xsrf.yaml' + - $ref: '../components/parameters/space_id.yaml' requestBody: required: true content: @@ -29,4 +30,3 @@ post: application/json: schema: $ref: '../components/schemas/400_response.yaml' - diff --git a/src/plugins/data_views/docs/openapi/paths/api@data_views@data_view@{viewid}.yaml b/src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views@data_view@{viewid}.yaml similarity index 94% rename from src/plugins/data_views/docs/openapi/paths/api@data_views@data_view@{viewid}.yaml rename to src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views@data_view@{viewid}.yaml index e239978f39f87..157899bcb578f 100644 --- a/src/plugins/data_views/docs/openapi/paths/api@data_views@data_view@{viewid}.yaml +++ b/src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views@data_view@{viewid}.yaml @@ -7,6 +7,7 @@ get: - data views parameters: - $ref: '../components/parameters/view_id.yaml' + - $ref: '../components/parameters/space_id.yaml' responses: '200': description: Indicates a successful call. @@ -35,6 +36,7 @@ delete: parameters: - $ref: '../components/headers/kbn_xsrf.yaml' - $ref: '../components/parameters/view_id.yaml' + - $ref: '../components/parameters/space_id.yaml' responses: '204': description: Indicates a successful call. @@ -55,6 +57,7 @@ post: parameters: - $ref: '../components/headers/kbn_xsrf.yaml' - $ref: '../components/parameters/view_id.yaml' + - $ref: '../components/parameters/space_id.yaml' requestBody: required: true content: diff --git a/src/plugins/data_views/docs/openapi/paths/api@data_views@data_view@{viewid}@fields.yaml b/src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views@data_view@{viewid}@fields.yaml similarity index 92% rename from src/plugins/data_views/docs/openapi/paths/api@data_views@data_view@{viewid}@fields.yaml rename to src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views@data_view@{viewid}@fields.yaml index 62bce25d96cd9..5e7911e6b7aec 100644 --- a/src/plugins/data_views/docs/openapi/paths/api@data_views@data_view@{viewid}@fields.yaml +++ b/src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views@data_view@{viewid}@fields.yaml @@ -8,6 +8,7 @@ post: parameters: - $ref: '../components/headers/kbn_xsrf.yaml' - $ref: '../components/parameters/view_id.yaml' + - $ref: '../components/parameters/space_id.yaml' requestBody: required: true content: @@ -38,4 +39,4 @@ post: content: application/json: schema: - $ref: '../components/schemas/400_response.yaml' \ No newline at end of file + $ref: '../components/schemas/400_response.yaml' diff --git a/src/plugins/data_views/docs/openapi/paths/api@data_views@data_view@{viewid}@runtime_field.yaml b/src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views@data_view@{viewid}@runtime_field.yaml similarity index 96% rename from src/plugins/data_views/docs/openapi/paths/api@data_views@data_view@{viewid}@runtime_field.yaml rename to src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views@data_view@{viewid}@runtime_field.yaml index 04f836cae94fe..dcc2783a82afa 100644 --- a/src/plugins/data_views/docs/openapi/paths/api@data_views@data_view@{viewid}@runtime_field.yaml +++ b/src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views@data_view@{viewid}@runtime_field.yaml @@ -8,6 +8,7 @@ post: parameters: - $ref: '../components/headers/kbn_xsrf.yaml' - $ref: '../components/parameters/view_id.yaml' + - $ref: '../components/parameters/space_id.yaml' requestBody: required: true content: @@ -39,6 +40,7 @@ put: - data views parameters: - $ref: '../components/headers/kbn_xsrf.yaml' + - $ref: '../components/parameters/space_id.yaml' - name: viewId in: path description: | diff --git a/src/plugins/data_views/docs/openapi/paths/api@data_views@data_view@{viewid}@runtime_field@{fieldname}.yaml b/src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views@data_view@{viewid}@runtime_field@{fieldname}.yaml similarity index 95% rename from src/plugins/data_views/docs/openapi/paths/api@data_views@data_view@{viewid}@runtime_field@{fieldname}.yaml rename to src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views@data_view@{viewid}@runtime_field@{fieldname}.yaml index 2f6a452d351a4..7508613089a47 100644 --- a/src/plugins/data_views/docs/openapi/paths/api@data_views@data_view@{viewid}@runtime_field@{fieldname}.yaml +++ b/src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views@data_view@{viewid}@runtime_field@{fieldname}.yaml @@ -8,6 +8,7 @@ get: parameters: - $ref: '../components/parameters/field_name.yaml' - $ref: '../components/parameters/view_id.yaml' + - $ref: '../components/parameters/space_id.yaml' responses: '200': description: Indicates a successful call. @@ -42,6 +43,7 @@ delete: parameters: - $ref: '../components/parameters/field_name.yaml' - $ref: '../components/parameters/view_id.yaml' + - $ref: '../components/parameters/space_id.yaml' responses: '200': description: Indicates a successful call. @@ -62,6 +64,7 @@ post: parameters: - $ref: '../components/parameters/field_name.yaml' - $ref: '../components/parameters/view_id.yaml' + - $ref: '../components/parameters/space_id.yaml' requestBody: required: true content: diff --git a/src/plugins/data_views/docs/openapi/paths/api@data_views@default.yaml b/src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views@default.yaml similarity index 95% rename from src/plugins/data_views/docs/openapi/paths/api@data_views@default.yaml rename to src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views@default.yaml index 83e616b635973..6664e1fc45f97 100644 --- a/src/plugins/data_views/docs/openapi/paths/api@data_views@default.yaml +++ b/src/plugins/data_views/docs/openapi/paths/s@{spaceid}@api@data_views@default.yaml @@ -5,6 +5,8 @@ get: This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. tags: - data views + parameters: + - $ref: '../components/parameters/space_id.yaml' responses: '200': description: Indicates a successful call. @@ -33,6 +35,7 @@ post: - data views parameters: - $ref: '../components/headers/kbn_xsrf.yaml' + - $ref: '../components/parameters/space_id.yaml' requestBody: required: true content: diff --git a/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts b/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts index aca312c8ea64d..15ee00e5118cb 100644 --- a/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts +++ b/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts @@ -32,6 +32,7 @@ export interface FieldDescriptor { timeZone?: string[]; timeSeriesMetric?: estypes.MappingTimeSeriesMetricType; timeSeriesDimension?: boolean; + defaultFormatter?: string; } interface FieldSubType { diff --git a/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_caps_response.test.js b/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_caps_response.test.js index e0722125cf7d6..61f345876ac8d 100644 --- a/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_caps_response.test.js +++ b/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_caps_response.test.js @@ -165,5 +165,22 @@ describe('index_patterns/field_capabilities/field_caps_response', () => { expect(child).not.toHaveProperty('subType'); }); }); + + it('sets default field formatter', () => { + const fields = readFieldCapsResponse({ + fields: { + seconds: { + long: { + searchable: true, + aggregatable: true, + meta: { + unit: ['s'], + }, + }, + }, + }, + }); + expect(fields[0].defaultFormatter).toEqual('s'); + }); }); }); diff --git a/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_caps_response.ts b/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_caps_response.ts index 715fea9beef3b..805dbb9deb5fd 100644 --- a/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_caps_response.ts +++ b/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_caps_response.ts @@ -12,6 +12,11 @@ import { castEsToKbnFieldTypeName } from '@kbn/field-types'; import { shouldReadFieldFromDocValues } from './should_read_field_from_doc_values'; import { FieldDescriptor } from '../..'; +// The array will have different values if values vary across indices +const unitsArrayToFormatter = (unitArr: string[]) => { + return unitArr.find((unit) => unitArr[0] !== unit) ? undefined : unitArr[0]; +}; + /** * Read the response from the _field_caps API to determine the type and * "aggregatable"/"searchable" status of each field. @@ -134,7 +139,11 @@ export function readFieldCapsResponse( timeSeriesMetricType = 'position'; } const esType = types[0]; - const field = { + + const defaultFormatter = + capsByType[types[0]].meta?.unit && unitsArrayToFormatter(capsByType[types[0]].meta?.unit); + + const field: FieldDescriptor = { name: fieldName, type: castEsToKbnFieldTypeName(esType), esTypes: types, @@ -147,6 +156,11 @@ export function readFieldCapsResponse( timeSeriesMetric: timeSeriesMetricType, timeSeriesDimension: capsByType[types[0]].time_series_dimension, }; + + if (defaultFormatter) { + field.defaultFormatter = defaultFormatter; + } + // This is intentionally using a "hash" and a "push" to be highly optimized with very large indexes agg.array.push(field); agg.hash[fieldName] = field; diff --git a/src/plugins/data_views/server/rest_api_routes/internal/fields_for.ts b/src/plugins/data_views/server/rest_api_routes/internal/fields_for.ts index 98b4086bf161e..1d5f8e636315a 100644 --- a/src/plugins/data_views/server/rest_api_routes/internal/fields_for.ts +++ b/src/plugins/data_views/server/rest_api_routes/internal/fields_for.ts @@ -99,6 +99,7 @@ const FieldDescriptorSchema = schema.object({ conflictDescriptions: schema.maybe( schema.recordOf(schema.string(), schema.arrayOf(schema.string())) ), + defaultFormatter: schema.maybe(schema.string()), }); export const validate: FullValidationConfig = { diff --git a/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts b/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts index 1540fe9f3fe8c..49e010d13a041 100644 --- a/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts +++ b/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts @@ -18,7 +18,7 @@ import { FetchStatus } from '../../types'; const MAX_NUM_OF_COLUMNS = 50; // For ES|QL we want in case of the following commands to display a table view, otherwise display a document view -const TRANSFORMATIONAL_COMMANDS = ['stats', 'project', 'keep']; +const TRANSFORMATIONAL_COMMANDS = ['stats', 'keep']; /** * Hook to take care of text based query language state transformations when a new result is returned diff --git a/src/plugins/embeddable/public/add_panel_flyout/add_panel_flyout.test.tsx b/src/plugins/embeddable/public/add_panel_flyout/add_panel_flyout.test.tsx index a39f4ee95f15b..3b8052c88a798 100644 --- a/src/plugins/embeddable/public/add_panel_flyout/add_panel_flyout.test.tsx +++ b/src/plugins/embeddable/public/add_panel_flyout/add_panel_flyout.test.tsx @@ -68,7 +68,7 @@ describe('add panel flyout', () => { getEmbeddableFactory: embeddableStart.getEmbeddableFactory, } ); - container.addNewEmbeddable = jest.fn(); + container.addNewEmbeddable = jest.fn().mockResolvedValue({ id: 'foo' }); }); test('add panel flyout renders SavedObjectFinder', async () => { diff --git a/src/plugins/embeddable/public/add_panel_flyout/add_panel_flyout.tsx b/src/plugins/embeddable/public/add_panel_flyout/add_panel_flyout.tsx index f0554fed61782..a407fb025e2c8 100644 --- a/src/plugins/embeddable/public/add_panel_flyout/add_panel_flyout.tsx +++ b/src/plugins/embeddable/public/add_panel_flyout/add_panel_flyout.tsx @@ -32,6 +32,7 @@ import { SavedObjectEmbeddableInput, EmbeddableFactoryNotFoundError, } from '../lib'; +import { savedObjectToPanel } from '../registry/saved_object_to_panel_methods'; type FactoryMap = { [key: string]: EmbeddableFactory }; @@ -101,12 +102,30 @@ export const AddPanelFlyout = ({ throw new EmbeddableFactoryNotFoundError(type); } - const embeddable = await container.addNewEmbeddable( - factoryForSavedObjectType.type, - { savedObjectId: id }, - savedObject.attributes - ); - onAddPanel?.(embeddable.id); + let embeddableId: string; + + if (savedObjectToPanel[type]) { + // this panel type has a custom method for converting saved objects to panels + const panel = savedObjectToPanel[type](savedObject); + + const { id: _embeddableId } = await container.addNewEmbeddable( + factoryForSavedObjectType.type, + panel, + savedObject.attributes + ); + + embeddableId = _embeddableId; + } else { + const { id: _embeddableId } = await container.addNewEmbeddable( + factoryForSavedObjectType.type, + { savedObjectId: id }, + savedObject.attributes + ); + + embeddableId = _embeddableId; + } + + onAddPanel?.(embeddableId); showSuccessToast(name); runAddTelemetry(container.type, factoryForSavedObjectType, savedObject); @@ -136,6 +155,14 @@ export const AddPanelFlyout = ({ noItemsMessage={i18n.translate('embeddableApi.addPanel.noMatchingObjectsMessage', { defaultMessage: 'No matching objects found.', })} + getTooltipText={(item) => { + return item.managed + ? i18n.translate('embeddableApi.addPanel.managedPanelTooltip', { + defaultMessage: + 'This panel is managed by Elastic. It can be added but will be unlinked from the library.', + }) + : undefined; + }} /> diff --git a/src/plugins/embeddable/public/index.ts b/src/plugins/embeddable/public/index.ts index 5f9254ae11748..2eec70d591db3 100644 --- a/src/plugins/embeddable/public/index.ts +++ b/src/plugins/embeddable/public/index.ts @@ -113,6 +113,8 @@ export { serializeReactEmbeddableTitles, } from './react_embeddable_system'; +export { registerSavedObjectToPanelMethod } from './registry/saved_object_to_panel_methods'; + export function plugin(initializerContext: PluginInitializerContext) { return new EmbeddablePublicPlugin(initializerContext); } diff --git a/src/plugins/embeddable/public/lib/containers/container.ts b/src/plugins/embeddable/public/lib/containers/container.ts index d039bd5b80f89..9f3ac0f8d38a4 100644 --- a/src/plugins/embeddable/public/lib/containers/container.ts +++ b/src/plugins/embeddable/public/lib/containers/container.ts @@ -131,6 +131,16 @@ export abstract class Container< this.removeEmbeddable(id); } + public async addNewPanel( + panelPackage: PanelPackage + ): Promise { + const newEmbeddable = await this.addNewEmbeddable( + panelPackage.panelType, + panelPackage.initialState as Partial + ); + return newEmbeddable as ApiType; + } + public async replacePanel(idToRemove: string, { panelType, initialState }: PanelPackage) { return await this.replaceEmbeddable( idToRemove, diff --git a/src/plugins/embeddable/public/registry/saved_object_to_panel_methods.ts b/src/plugins/embeddable/public/registry/saved_object_to_panel_methods.ts new file mode 100644 index 0000000000000..2d996666d4042 --- /dev/null +++ b/src/plugins/embeddable/public/registry/saved_object_to_panel_methods.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { SavedObjectCommon } from '@kbn/saved-objects-finder-plugin/common'; + +type SavedObjectToPanelMethod = ( + savedObject: SavedObjectCommon +) => { savedObjectId: string } | Partial; + +export const savedObjectToPanel: Record> = {}; + +export const registerSavedObjectToPanelMethod = ( + savedObjectType: string, + method: SavedObjectToPanelMethod +) => { + savedObjectToPanel[savedObjectType] = method; +}; diff --git a/src/plugins/kibana_react/public/url_template_editor/styles.scss b/src/plugins/kibana_react/public/url_template_editor/styles.scss index 99379b21454ec..1bff881958076 100644 --- a/src/plugins/kibana_react/public/url_template_editor/styles.scss +++ b/src/plugins/kibana_react/public/url_template_editor/styles.scss @@ -1,5 +1,5 @@ .urlTemplateEditor__container { .monaco-editor .lines-content.monaco-editor-background { - margin: $euiSizeS; + margin: 0 $euiSizeS; } } diff --git a/src/plugins/kibana_react/public/url_template_editor/url_template_editor.tsx b/src/plugins/kibana_react/public/url_template_editor/url_template_editor.tsx index 770753bc8c35c..13773396eba76 100644 --- a/src/plugins/kibana_react/public/url_template_editor/url_template_editor.tsx +++ b/src/plugins/kibana_react/public/url_template_editor/url_template_editor.tsx @@ -22,6 +22,7 @@ export interface UrlTemplateEditorVariable { export interface UrlTemplateEditorProps { value: string; height?: CodeEditorProps['height']; + fitToContent?: CodeEditorProps['fitToContent']; variables?: UrlTemplateEditorVariable[]; onChange: CodeEditorProps['onChange']; onEditor?: (editor: monaco.editor.IStandaloneCodeEditor) => void; @@ -31,6 +32,7 @@ export interface UrlTemplateEditorProps { export const UrlTemplateEditor: React.FC = ({ height = 105, + fitToContent, value, variables, onChange, @@ -127,6 +129,7 @@ export const UrlTemplateEditor: React.FC = ({ = ({ }, wordWrap: 'on', wrappingIndent: 'none', + automaticLayout: true, + scrollBeyondLastLine: false, + overviewRulerLanes: 0, + padding: { top: 8, bottom: 8 }, }} /> diff --git a/src/plugins/saved_objects_finder/public/finder/saved_object_finder.test.tsx b/src/plugins/saved_objects_finder/public/finder/saved_object_finder.test.tsx index 8ac5715263e30..c3e143ddbac42 100644 --- a/src/plugins/saved_objects_finder/public/finder/saved_object_finder.test.tsx +++ b/src/plugins/saved_objects_finder/public/finder/saved_object_finder.test.tsx @@ -9,7 +9,12 @@ const nextTick = () => new Promise((res) => process.nextTick(res)); import lodash from 'lodash'; -jest.spyOn(lodash, 'debounce').mockImplementation((fn: any) => fn); +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +jest.spyOn(lodash, 'debounce').mockImplementation((fn: any) => { + fn.cancel = jest.fn(); + return fn; +}); import { EuiInMemoryTable, EuiLink, @@ -962,4 +967,36 @@ describe('SavedObjectsFinder', () => { expect(findTestSubject(wrapper, 'tableHeaderCell_references_2')).toHaveLength(0); }); }); + + it('should add a tooltip when text is provided', async () => { + (contentClient.mSearch as any as jest.SpyInstance).mockResolvedValue({ + hits: [doc, doc2, doc3], + }); + + const tooltipText = 'This is a tooltip'; + + render( + (item.id === doc3.id ? tooltipText : undefined)} + /> + ); + + const assertTooltip = async (linkTitle: string, show: boolean) => { + const elem = await screen.findByText(linkTitle); + userEvent.hover(elem); + + const tooltip = screen.queryByText(tooltipText); + if (show) { + expect(tooltip).toBeInTheDocument(); + } else { + expect(tooltip).toBeNull(); + } + }; + + assertTooltip(doc.attributes.title, false); + assertTooltip(doc2.attributes.title, false); + assertTooltip(doc3.attributes.title, true); + }); }); diff --git a/src/plugins/saved_objects_finder/public/finder/saved_object_finder.tsx b/src/plugins/saved_objects_finder/public/finder/saved_object_finder.tsx index d660a8bf2857a..034f63083be1d 100644 --- a/src/plugins/saved_objects_finder/public/finder/saved_object_finder.tsx +++ b/src/plugins/saved_objects_finder/public/finder/saved_object_finder.tsx @@ -77,6 +77,7 @@ interface BaseSavedObjectFinder { leftChildren?: ReactElement | ReactElement[]; children?: ReactElement | ReactElement[]; helpText?: string; + getTooltipText?: (item: SavedObjectFinderItem) => string | undefined; } interface SavedObjectFinderFixedPage extends BaseSavedObjectFinder { @@ -288,7 +289,7 @@ export class SavedObjectFinderUi extends React.Component< ? currentSavedObjectMetaData.getTooltipForSavedObject(item.simple) : `${item.name} (${currentSavedObjectMetaData!.name})`; - return ( + const link = ( ); + + const tooltipText = this.props.getTooltipText?.(item); + + return tooltipText ? ( + + {link} + + ) : ( + link + ); }, }, ...(tagColumn ? [tagColumn] : []), diff --git a/src/plugins/saved_search/public/plugin.ts b/src/plugins/saved_search/public/plugin.ts index 8e8dd697cc55c..5b2d1e4e79fbf 100644 --- a/src/plugins/saved_search/public/plugin.ts +++ b/src/plugins/saved_search/public/plugin.ts @@ -17,13 +17,14 @@ import type { ContentManagementPublicStart, } from '@kbn/content-management-plugin/public'; import type { SOWithMetadata } from '@kbn/content-management-utils'; -import type { EmbeddableStart } from '@kbn/embeddable-plugin/public'; +import { EmbeddableStart, registerSavedObjectToPanelMethod } from '@kbn/embeddable-plugin/public'; import { getSavedSearch, saveSavedSearch, SaveSavedSearchOptions, getNewSavedSearch, SavedSearchUnwrapResult, + SearchByValueInput, } from './services/saved_searches'; import { SavedSearch, SavedSearchAttributes } from '../common/types'; import { SavedSearchType, LATEST_VERSION } from '../common'; @@ -35,6 +36,7 @@ import { getSavedSearchAttributeService, toSavedSearch, } from './services/saved_searches'; +import { savedObjectToEmbeddableAttributes } from './services/saved_searches/saved_search_attribute_service'; /** * Saved search plugin public Setup contract @@ -115,6 +117,19 @@ export class SavedSearchPublicPlugin expressions.registerType(kibanaContext); + registerSavedObjectToPanelMethod( + SavedSearchType, + (savedObject) => { + if (!savedObject.managed) { + return { savedObjectId: savedObject.id }; + } + + return { + attributes: savedObjectToEmbeddableAttributes(savedObject), + }; + } + ); + return {}; } diff --git a/src/plugins/saved_search/public/services/saved_searches/saved_search_attribute_service.ts b/src/plugins/saved_search/public/services/saved_searches/saved_search_attribute_service.ts index aff78e18b6bd1..726853d26ebe4 100644 --- a/src/plugins/saved_search/public/services/saved_searches/saved_search_attribute_service.ts +++ b/src/plugins/saved_search/public/services/saved_searches/saved_search_attribute_service.ts @@ -9,6 +9,8 @@ import type { AttributeService, EmbeddableStart } from '@kbn/embeddable-plugin/public'; import type { OnSaveProps } from '@kbn/saved-objects-plugin/public'; import { SEARCH_EMBEDDABLE_TYPE } from '@kbn/discover-utils'; +import { SavedObjectCommon } from '@kbn/saved-objects-finder-plugin/common'; +import { SavedSearchAttributes } from '../../../common'; import type { SavedSearch, SavedSearchByValueAttributes, @@ -41,6 +43,13 @@ export type SavedSearchAttributeService = AttributeService< SavedSearchUnwrapMetaInfo >; +export const savedObjectToEmbeddableAttributes = ( + savedObject: SavedObjectCommon +) => ({ + ...savedObject.attributes, + references: savedObject.references, +}); + export function getSavedSearchAttributeService( services: SavedSearchesServiceDeps & { embeddable: EmbeddableStart; @@ -67,10 +76,7 @@ export function getSavedSearchAttributeService( const so = await getSearchSavedObject(savedObjectId, createGetSavedSearchDeps(services)); return { - attributes: { - ...so.item.attributes, - references: so.item.references, - }, + attributes: savedObjectToEmbeddableAttributes(so.item), metaInfo: { sharingSavedObjectProps: so.meta, managed: so.item.managed, diff --git a/src/plugins/saved_search/tsconfig.json b/src/plugins/saved_search/tsconfig.json index b1aa1679469ee..c4ea0eaf4d5bc 100644 --- a/src/plugins/saved_search/tsconfig.json +++ b/src/plugins/saved_search/tsconfig.json @@ -32,6 +32,7 @@ "@kbn/logging", "@kbn/core-plugins-server", "@kbn/utility-types", + "@kbn/saved-objects-finder-plugin", ], "exclude": [ "target/**/*", diff --git a/src/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/url_drilldown_collect_config.tsx b/src/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/url_drilldown_collect_config.tsx index 0495f2d61063c..4db2510ba22e5 100644 --- a/src/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/url_drilldown_collect_config.tsx +++ b/src/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/url_drilldown_collect_config.tsx @@ -88,6 +88,7 @@ export const UrlDrilldownCollectConfig: React.FC labelAppend={variablesDropdown} > { let queryHasTransformationalCommands = false; diff --git a/src/plugins/vis_default_editor/public/components/controls/palette_picker.tsx b/src/plugins/vis_default_editor/public/components/controls/palette_picker.tsx index 29d8924a25077..6155cdbd24cb6 100644 --- a/src/plugins/vis_default_editor/public/components/controls/palette_picker.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/palette_picker.tsx @@ -8,6 +8,7 @@ import React from 'react'; import type { PaletteOutput, PaletteRegistry } from '@kbn/coloring'; +import { getActivePaletteName } from '@kbn/coloring'; import { EuiColorPalettePicker, EuiColorPalettePickerPaletteProps } from '@elastic/eui'; import { EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -27,6 +28,8 @@ export function PalettePicker({ paramName, setPalette, }: PalettePickerProps) { + const paletteName = getActivePaletteName(activePalette?.name); + const palettesList: EuiColorPalettePickerPaletteProps[] = palettes .getAll() .filter(({ internal }) => !internal) @@ -35,10 +38,7 @@ export function PalettePicker({ value: id, title, type: 'fixed', - palette: getCategoricalColors( - 10, - id === activePalette?.name ? activePalette?.params : undefined - ), + palette: getCategoricalColors(10, id === paletteName ? activePalette?.params : undefined), }; }); @@ -61,7 +61,7 @@ export function PalettePicker({ name: palette?.value ?? DEFAULT_PALETTE, }); }} - valueOfSelected={activePalette?.name || DEFAULT_PALETTE} + valueOfSelected={paletteName} selectionDisplay={'palette'} /> diff --git a/src/plugins/vis_types/timeseries/public/application/components/palette_picker.tsx b/src/plugins/vis_types/timeseries/public/application/components/palette_picker.tsx index 56d763ad64eb5..cd459de244f84 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/palette_picker.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/palette_picker.tsx @@ -8,6 +8,7 @@ import React from 'react'; import type { PaletteRegistry, PaletteOutput } from '@kbn/coloring'; +import { getActivePaletteName } from '@kbn/coloring'; import { EuiColorPalettePicker } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { rainbowColors } from '../lib/rainbow_colors'; @@ -23,6 +24,7 @@ export interface PalettePickerProps { export function PalettePicker({ activePalette, palettes, setPalette, color }: PalettePickerProps) { const finalGradientColor = computeGradientFinalColor(color); + const paletteName = getActivePaletteName(activePalette?.name); return ( ); diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index cd840302ff01d..594ea6ca9488a 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -47,7 +47,11 @@ import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; import type { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { ExpressionsSetup, ExpressionsStart } from '@kbn/expressions-plugin/public'; -import type { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public'; +import { + EmbeddableSetup, + EmbeddableStart, + registerSavedObjectToPanelMethod, +} from '@kbn/embeddable-plugin/public'; import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; import type { NavigationPublicPluginStart as NavigationStart } from '@kbn/navigation-plugin/public'; import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; @@ -112,8 +116,14 @@ import { } from './services'; import { VisualizeConstants } from '../common/constants'; import { EditInLensAction } from './actions/edit_in_lens_action'; -import { ListingViewRegistry } from './types'; -import { LATEST_VERSION, CONTENT_ID } from '../common/content_management'; +import { ListingViewRegistry, SerializedVis } from './types'; +import { + LATEST_VERSION, + CONTENT_ID, + VisualizationSavedObjectAttributes, +} from '../common/content_management'; +import { SerializedVisData } from '../common'; +import { VisualizeByValueInput } from './embeddable/visualize_embeddable'; /** * Interface for this plugin's returned setup/start contracts. @@ -397,6 +407,37 @@ export class VisualizationsPlugin name: 'Visualize Library', }); + registerSavedObjectToPanelMethod( + CONTENT_ID, + (savedObject) => { + const visState = savedObject.attributes.visState; + + // not sure if visState actually is ever undefined, but following the type + if (!savedObject.managed || !visState) { + return { + savedObjectId: savedObject.id, + }; + } + + // data is not always defined, so I added a default value since the extract + // routine in the embeddable factory expects it to be there + const savedVis = JSON.parse(visState) as Omit & { + data?: SerializedVisData; + }; + + if (!savedVis.data) { + savedVis.data = { + searchSource: {}, + aggs: [], + }; + } + + return { + savedVis: savedVis as SerializedVis, // now we're sure we have "data" prop + }; + } + ); + return { ...this.types.setup(), visEditorsRegistry, diff --git a/test/api_integration/apis/search/bsearch.ts b/test/api_integration/apis/search/bsearch.ts index 867ae83864a74..96b4bbbf622cf 100644 --- a/test/api_integration/apis/search/bsearch.ts +++ b/test/api_integration/apis/search/bsearch.ts @@ -428,7 +428,6 @@ export default function ({ getService }: FtrProviderContext) { expect(jsonBody[0].result.requestParams).to.eql({ method: 'POST', path: '/_query', - querystring: 'drop_null_columns', }); }); @@ -457,7 +456,6 @@ export default function ({ getService }: FtrProviderContext) { expect(jsonBody[0].error.attributes.requestParams).to.eql({ method: 'POST', path: '/_query', - querystring: 'drop_null_columns', }); }); }); diff --git a/test/functional/apps/bundles/index.js b/test/functional/apps/bundles/index.js index aa175f16e5d48..7363d9f0b5256 100644 --- a/test/functional/apps/bundles/index.js +++ b/test/functional/apps/bundles/index.js @@ -14,38 +14,38 @@ export default function ({ getService }) { const supertest = getService('supertest'); describe('bundle compression', function () { - let buildNum; + let buildHash; before(async () => { const resp = await supertest.get('/api/status').expect(200); - buildNum = resp.body.version.build_number; + buildHash = resp.body.version.build_hash.slice(0, 12); }); it('returns gzip files when client only supports gzip', () => supertest // We use the kbn-ui-shared-deps for these tests since they are always built with br compressed outputs, // even in dev. Bundles built by @kbn/optimizer are only built with br compression in dist mode. - .get(`/${buildNum}/bundles/kbn-ui-shared-deps-npm/kbn-ui-shared-deps-npm.dll.js`) + .get(`/${buildHash}/bundles/kbn-ui-shared-deps-npm/kbn-ui-shared-deps-npm.dll.js`) .set('Accept-Encoding', 'gzip') .expect(200) .expect('Content-Encoding', 'gzip')); it('returns br files when client only supports br', () => supertest - .get(`/${buildNum}/bundles/kbn-ui-shared-deps-npm/kbn-ui-shared-deps-npm.dll.js`) + .get(`/${buildHash}/bundles/kbn-ui-shared-deps-npm/kbn-ui-shared-deps-npm.dll.js`) .set('Accept-Encoding', 'br') .expect(200) .expect('Content-Encoding', 'br')); it('returns br files when client only supports gzip and br', () => supertest - .get(`/${buildNum}/bundles/kbn-ui-shared-deps-npm/kbn-ui-shared-deps-npm.dll.js`) + .get(`/${buildHash}/bundles/kbn-ui-shared-deps-npm/kbn-ui-shared-deps-npm.dll.js`) .set('Accept-Encoding', 'gzip, br') .expect(200) .expect('Content-Encoding', 'br')); it('returns gzip files when client prefers gzip', () => supertest - .get(`/${buildNum}/bundles/kbn-ui-shared-deps-npm/kbn-ui-shared-deps-npm.dll.js`) + .get(`/${buildHash}/bundles/kbn-ui-shared-deps-npm/kbn-ui-shared-deps-npm.dll.js`) .set('Accept-Encoding', 'gzip;q=1.0, br;q=0.5') .expect(200) .expect('Content-Encoding', 'gzip')); diff --git a/test/functional/apps/management/data_views/_data_view_create_delete.ts b/test/functional/apps/management/data_views/_data_view_create_delete.ts index e3bc2240887ad..651dbce7ada02 100644 --- a/test/functional/apps/management/data_views/_data_view_create_delete.ts +++ b/test/functional/apps/management/data_views/_data_view_create_delete.ts @@ -20,7 +20,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const es = getService('es'); const PageObjects = getPageObjects(['settings', 'common', 'header']); - describe('creating and deleting default data view', function describeIndexTests() { + // FLAKY: https://github.com/elastic/kibana/issues/174066 + describe.skip('creating and deleting default data view', function describeIndexTests() { before(async function () { await esArchiver.emptyKibanaIndex(); await esArchiver.loadIfNeeded( diff --git a/test/functional/apps/management/data_views/_field_formatter.ts b/test/functional/apps/management/data_views/_field_formatter.ts index dfcdc0b877581..cddbe8ccf5ce4 100644 --- a/test/functional/apps/management/data_views/_field_formatter.ts +++ b/test/functional/apps/management/data_views/_field_formatter.ts @@ -413,6 +413,51 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ]); }); }); + + describe('default formatter by field meta value', () => { + const indexTitle = 'field_formats_management_functional_tests'; + + before(async () => { + if (await es.indices.exists({ index: indexTitle })) { + await es.indices.delete({ index: indexTitle }); + } + }); + + it('should apply default formatter by field meta value', async () => { + await es.indices.create({ + index: indexTitle, + body: { + mappings: { + properties: { + seconds: { type: 'long', meta: { unit: 's' } }, + }, + }, + }, + }); + + const docResult = await es.index({ + index: indexTitle, + body: { seconds: 1234 }, + refresh: 'wait_for', + }); + + const testDocumentId = docResult._id; + + const indexPatternResult = await indexPatterns.create( + { title: `${indexTitle}*` }, // sidesteps field caching when index pattern is reused + { override: true } + ); + + await PageObjects.common.navigateToApp('discover', { + hash: `/doc/${indexPatternResult.id}/${indexTitle}?id=${testDocumentId}`, + }); + await testSubjects.exists('doc-hit'); + + const renderedValue = await testSubjects.find(`tableDocViewRow-seconds-value`); + const text = await renderedValue.getVisibleText(); + expect(text).to.be('20.57 min'); + }); + }); }); /** diff --git a/test/functional/fixtures/kbn_archiver/managed_content.json b/test/functional/fixtures/kbn_archiver/managed_content.json index 9e46c515d6b8b..d2cafbe0c842e 100644 --- a/test/functional/fixtures/kbn_archiver/managed_content.json +++ b/test/functional/fixtures/kbn_archiver/managed_content.json @@ -1,17 +1,19 @@ {"attributes":{"allowHidden":false,"fieldAttrs":"{}","fieldFormatMap":"{}","fields":"[]","name":"logstash-*","runtimeFieldMap":"{}","sourceFilters":"[]","timeFieldName":"@timestamp","title":"logstash-*"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-18T17:35:58.606Z","id":"5f863f70-4728-4e8d-b441-db08f8c33b28","managed":false,"references":[],"type":"index-pattern","typeMigrationVersion":"8.0.0","updated_at":"2024-01-18T17:35:58.606Z","version":"WzI4LDFd"} -{"attributes":{"description":"","state":{"adHocDataViews":{},"datasourceStates":{"formBased":{"layers":{"e633b1af-3ab4-4bf5-8faa-fefde06c4a4a":{"columnOrder":["f2555a1a-6f93-43fd-bc63-acdfadd47729","d229daf9-9658-4579-99af-01d8adb2f25f"],"columns":{"d229daf9-9658-4579-99af-01d8adb2f25f":{"dataType":"number","isBucketed":false,"label":"Median of bytes","operationType":"median","params":{"emptyAsNull":true},"scale":"ratio","sourceField":"bytes"},"f2555a1a-6f93-43fd-bc63-acdfadd47729":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"dropPartials":false,"includeEmptyRows":true,"interval":"auto"},"scale":"interval","sourceField":"@timestamp"}},"incompleteColumns":{},"sampling":1}}},"indexpattern":{"layers":{}},"textBased":{"layers":{}}},"filters":[],"internalReferences":[],"query":{"language":"kuery","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"labelsOrientation":{"x":0,"yLeft":0,"yRight":0},"layers":[{"accessors":["d229daf9-9658-4579-99af-01d8adb2f25f"],"layerId":"e633b1af-3ab4-4bf5-8faa-fefde06c4a4a","layerType":"data","position":"top","seriesType":"bar_stacked","showGridlines":false,"xAccessor":"f2555a1a-6f93-43fd-bc63-acdfadd47729"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"bar_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"Lens vis (managed)","visualizationType":"lnsXY"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-18T17:42:12.920Z","id":"managed-36db-4a3b-a4ba-7a64ab8f130b","managed":true,"references":[{"id":"5f863f70-4728-4e8d-b441-db08f8c33b28","name":"indexpattern-datasource-layer-e633b1af-3ab4-4bf5-8faa-fefde06c4a4a","type":"index-pattern"}],"type":"lens","typeMigrationVersion":"8.9.0","updated_at":"2024-01-18T17:42:12.920Z","version":"WzQ1LDFd"} +{"attributes":{"description":"","state":{"adHocDataViews":{},"datasourceStates":{"formBased":{"layers":{"e633b1af-3ab4-4bf5-8faa-fefde06c4a4a":{"columnOrder":["f2555a1a-6f93-43fd-bc63-acdfadd47729","d229daf9-9658-4579-99af-01d8adb2f25f"],"columns":{"d229daf9-9658-4579-99af-01d8adb2f25f":{"dataType":"number","isBucketed":false,"label":"Median of bytes","operationType":"median","params":{"emptyAsNull":true},"scale":"ratio","sourceField":"bytes"},"f2555a1a-6f93-43fd-bc63-acdfadd47729":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"dropPartials":false,"includeEmptyRows":true,"interval":"auto"},"scale":"interval","sourceField":"@timestamp"}},"incompleteColumns":{},"sampling":1}}},"indexpattern":{"layers":{}},"textBased":{"layers":{}}},"filters":[],"internalReferences":[],"query":{"language":"kuery","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"labelsOrientation":{"x":0,"yLeft":0,"yRight":0},"layers":[{"accessors":["d229daf9-9658-4579-99af-01d8adb2f25f"],"layerId":"e633b1af-3ab4-4bf5-8faa-fefde06c4a4a","layerType":"data","position":"top","seriesType":"bar_stacked","showGridlines":false,"xAccessor":"f2555a1a-6f93-43fd-bc63-acdfadd47729"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"bar_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"Managed lens vis","visualizationType":"lnsXY"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-18T17:42:12.920Z","id":"managed-36db-4a3b-a4ba-7a64ab8f130b","managed":true,"references":[{"id":"5f863f70-4728-4e8d-b441-db08f8c33b28","name":"indexpattern-datasource-layer-e633b1af-3ab4-4bf5-8faa-fefde06c4a4a","type":"index-pattern"}],"type":"lens","typeMigrationVersion":"8.9.0","updated_at":"2024-01-18T17:42:12.920Z","version":"WzQ1LDFd"} -{"attributes":{"description":"","state":{"adHocDataViews":{},"datasourceStates":{"formBased":{"layers":{"e633b1af-3ab4-4bf5-8faa-fefde06c4a4a":{"columnOrder":["f2555a1a-6f93-43fd-bc63-acdfadd47729","d229daf9-9658-4579-99af-01d8adb2f25f"],"columns":{"d229daf9-9658-4579-99af-01d8adb2f25f":{"dataType":"number","isBucketed":false,"label":"Median of bytes","operationType":"median","params":{"emptyAsNull":true},"scale":"ratio","sourceField":"bytes"},"f2555a1a-6f93-43fd-bc63-acdfadd47729":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"dropPartials":false,"includeEmptyRows":true,"interval":"auto"},"scale":"interval","sourceField":"@timestamp"}},"incompleteColumns":{},"sampling":1}}},"indexpattern":{"layers":{}},"textBased":{"layers":{}}},"filters":[],"internalReferences":[],"query":{"language":"kuery","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"labelsOrientation":{"x":0,"yLeft":0,"yRight":0},"layers":[{"accessors":["d229daf9-9658-4579-99af-01d8adb2f25f"],"layerId":"e633b1af-3ab4-4bf5-8faa-fefde06c4a4a","layerType":"data","position":"top","seriesType":"bar_stacked","showGridlines":false,"xAccessor":"f2555a1a-6f93-43fd-bc63-acdfadd47729"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"bar_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"Lens vis (unmanaged)","visualizationType":"lnsXY"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-18T17:42:12.920Z","id":"unmanaged-36db-4a3b-a4ba-7a64ab8f130b","managed":false,"references":[{"id":"5f863f70-4728-4e8d-b441-db08f8c33b28","name":"indexpattern-datasource-layer-e633b1af-3ab4-4bf5-8faa-fefde06c4a4a","type":"index-pattern"}],"type":"lens","typeMigrationVersion":"8.9.0","updated_at":"2024-01-18T17:42:12.920Z","version":"WzQ1LDFd"} +{"attributes":{"description":"","state":{"adHocDataViews":{},"datasourceStates":{"formBased":{"layers":{"e633b1af-3ab4-4bf5-8faa-fefde06c4a4a":{"columnOrder":["f2555a1a-6f93-43fd-bc63-acdfadd47729","d229daf9-9658-4579-99af-01d8adb2f25f"],"columns":{"d229daf9-9658-4579-99af-01d8adb2f25f":{"dataType":"number","isBucketed":false,"label":"Median of bytes","operationType":"median","params":{"emptyAsNull":true},"scale":"ratio","sourceField":"bytes"},"f2555a1a-6f93-43fd-bc63-acdfadd47729":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"dropPartials":false,"includeEmptyRows":true,"interval":"auto"},"scale":"interval","sourceField":"@timestamp"}},"incompleteColumns":{},"sampling":1}}},"indexpattern":{"layers":{}},"textBased":{"layers":{}}},"filters":[],"internalReferences":[],"query":{"language":"kuery","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"labelsOrientation":{"x":0,"yLeft":0,"yRight":0},"layers":[{"accessors":["d229daf9-9658-4579-99af-01d8adb2f25f"],"layerId":"e633b1af-3ab4-4bf5-8faa-fefde06c4a4a","layerType":"data","position":"top","seriesType":"bar_stacked","showGridlines":false,"xAccessor":"f2555a1a-6f93-43fd-bc63-acdfadd47729"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"bar_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"Unmanaged lens vis","visualizationType":"lnsXY"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-18T17:42:12.920Z","id":"unmanaged-36db-4a3b-a4ba-7a64ab8f130b","managed":false,"references":[{"id":"5f863f70-4728-4e8d-b441-db08f8c33b28","name":"indexpattern-datasource-layer-e633b1af-3ab4-4bf5-8faa-fefde06c4a4a","type":"index-pattern"}],"type":"lens","typeMigrationVersion":"8.9.0","updated_at":"2024-01-18T17:42:12.920Z","version":"WzQ1LDFd"} -{"attributes":{"columns":["@tags","clientip"],"description":"","grid":{},"hideChart":false,"isTextBasedQuery":false,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"agent.raw:\\\"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)\\\" \",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["@timestamp","desc"]],"timeRestore":false,"title":"Saved search","usesAdHocDataView":false},"coreMigrationVersion":"8.8.0","created_at":"2024-01-22T18:11:05.016Z","id":"managed-3d62-4113-ac7c-de2e20a68fbc","managed":true,"references":[{"id":"5f863f70-4728-4e8d-b441-db08f8c33b28","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"search","typeMigrationVersion":"8.0.0","updated_at":"2024-01-22T18:11:05.016Z","version":"WzIzLDFd"} +{"attributes":{"columns":["@tags","clientip"],"description":"","grid":{},"hideChart":false,"isTextBasedQuery":false,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"agent.raw:\\\"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)\\\" \",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["@timestamp","desc"]],"timeRestore":false,"title":"Managed saved search","usesAdHocDataView":false},"coreMigrationVersion":"8.8.0","created_at":"2024-01-22T18:11:05.016Z","id":"managed-3d62-4113-ac7c-de2e20a68fbc","managed":true,"references":[{"id":"5f863f70-4728-4e8d-b441-db08f8c33b28","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"search","typeMigrationVersion":"8.0.0","updated_at":"2024-01-22T18:11:05.016Z","version":"WzIzLDFd"} -{"attributes":{"columns":["@tags","clientip"],"description":"","grid":{},"hideChart":false,"isTextBasedQuery":false,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"agent.raw:\\\"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)\\\" \",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["@timestamp","desc"]],"timeRestore":false,"title":"Saved search","usesAdHocDataView":false},"coreMigrationVersion":"8.8.0","created_at":"2024-01-22T18:11:05.016Z","id":"unmanaged-3d62-4113-ac7c-de2e20a68fbc","managed":false,"references":[{"id":"5f863f70-4728-4e8d-b441-db08f8c33b28","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"search","typeMigrationVersion":"8.0.0","updated_at":"2024-01-22T18:11:05.016Z","version":"WzIzLDFd"} +{"attributes":{"columns":["@tags","clientip"],"description":"","grid":{},"hideChart":false,"isTextBasedQuery":false,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"agent.raw:\\\"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)\\\" \",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["@timestamp","desc"]],"timeRestore":false,"title":"Unmanaged saved search","usesAdHocDataView":false},"coreMigrationVersion":"8.8.0","created_at":"2024-01-22T18:11:05.016Z","id":"unmanaged-3d62-4113-ac7c-de2e20a68fbc","managed":false,"references":[{"id":"5f863f70-4728-4e8d-b441-db08f8c33b28","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"search","typeMigrationVersion":"8.0.0","updated_at":"2024-01-22T18:11:05.016Z","version":"WzIzLDFd"} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"title":"Legacy visualization","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Legacy visualization\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"id\":\"1a14d0ad-0d74-4470-a189-8f040cddc1a1\",\"type\":\"timeseries\",\"series\":[{\"id\":\"daa8bbf7-86cc-4394-b249-be48da9f7351\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"palette\":{\"type\":\"palette\",\"name\":\"default\"},\"metrics\":[{\"id\":\"795375d9-1aa6-454d-9b23-687e69f3382c\",\"type\":\"count\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"default\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"override_index_pattern\":0,\"series_drop_last_bucket\":0}],\"time_field\":\"\",\"use_kibana_indexes\":true,\"interval\":\"\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"truncate_legend\":1,\"max_lines_legend\":1,\"show_grid\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":0,\"index_pattern_ref_name\":\"metrics_0_index_pattern\"}}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-24T18:53:06.818Z","id":"managed-feb9-4ba6-9538-1b8f67fb4f57","managed":true,"references":[{"id":"5f863f70-4728-4e8d-b441-db08f8c33b28","name":"metrics_0_index_pattern","type":"index-pattern"}],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-24T18:53:06.818Z","version":"WzEzLDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"title":"Managed legacy visualization","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Legacy visualization\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"id\":\"1a14d0ad-0d74-4470-a189-8f040cddc1a1\",\"type\":\"timeseries\",\"series\":[{\"id\":\"daa8bbf7-86cc-4394-b249-be48da9f7351\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"palette\":{\"type\":\"palette\",\"name\":\"default\"},\"metrics\":[{\"id\":\"795375d9-1aa6-454d-9b23-687e69f3382c\",\"type\":\"count\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"default\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"override_index_pattern\":0,\"series_drop_last_bucket\":0}],\"time_field\":\"\",\"use_kibana_indexes\":true,\"interval\":\"\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"truncate_legend\":1,\"max_lines_legend\":1,\"show_grid\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":0,\"index_pattern_ref_name\":\"metrics_0_index_pattern\"}}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-24T18:53:06.818Z","id":"managed-feb9-4ba6-9538-1b8f67fb4f57","managed":true,"references":[{"id":"5f863f70-4728-4e8d-b441-db08f8c33b28","name":"metrics_0_index_pattern","type":"index-pattern"}],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-24T18:53:06.818Z","version":"WzEzLDFd"} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"title":"Legacy visualization","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Legacy visualization\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"id\":\"1a14d0ad-0d74-4470-a189-8f040cddc1a1\",\"type\":\"timeseries\",\"series\":[{\"id\":\"daa8bbf7-86cc-4394-b249-be48da9f7351\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"palette\":{\"type\":\"palette\",\"name\":\"default\"},\"metrics\":[{\"id\":\"795375d9-1aa6-454d-9b23-687e69f3382c\",\"type\":\"count\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"default\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"override_index_pattern\":0,\"series_drop_last_bucket\":0}],\"time_field\":\"\",\"use_kibana_indexes\":true,\"interval\":\"\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"truncate_legend\":1,\"max_lines_legend\":1,\"show_grid\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":0,\"index_pattern_ref_name\":\"metrics_0_index_pattern\"}}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-24T18:53:06.818Z","id":"unmanaged-feb9-4ba6-9538-1b8f67fb4f57","managed":false,"references":[{"id":"5f863f70-4728-4e8d-b441-db08f8c33b28","name":"metrics_0_index_pattern","type":"index-pattern"}],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-24T18:53:06.818Z","version":"WzEzLDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"title":"Unmanaged legacy visualization","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Legacy visualization\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"id\":\"1a14d0ad-0d74-4470-a189-8f040cddc1a1\",\"type\":\"timeseries\",\"series\":[{\"id\":\"daa8bbf7-86cc-4394-b249-be48da9f7351\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"palette\":{\"type\":\"palette\",\"name\":\"default\"},\"metrics\":[{\"id\":\"795375d9-1aa6-454d-9b23-687e69f3382c\",\"type\":\"count\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"default\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"override_index_pattern\":0,\"series_drop_last_bucket\":0}],\"time_field\":\"\",\"use_kibana_indexes\":true,\"interval\":\"\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"truncate_legend\":1,\"max_lines_legend\":1,\"show_grid\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":0,\"index_pattern_ref_name\":\"metrics_0_index_pattern\"}}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-24T18:53:06.818Z","id":"unmanaged-feb9-4ba6-9538-1b8f67fb4f57","managed":false,"references":[{"id":"5f863f70-4728-4e8d-b441-db08f8c33b28","name":"metrics_0_index_pattern","type":"index-pattern"}],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-24T18:53:06.818Z","version":"WzEzLDFd"} -{"attributes":{"description":"","layerListJSON":"[{\"locale\":\"autoselect\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"isAutoSelect\":true,\"lightModeDefault\":\"road_map_desaturated\"},\"id\":\"5ff9c98e-e0d3-4aff-ac98-b33c191496b4\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"style\":{\"type\":\"EMS_VECTOR_TILE\",\"color\":\"\"},\"includeInFitToBounds\":true,\"type\":\"EMS_VECTOR_TILE\"}]","mapStateJSON":"{\"adHocDataViews\":[],\"zoom\":1.4,\"center\":{\"lon\":0,\"lat\":19.94277},\"timeFilters\":{\"from\":\"now-15m\",\"to\":\"now\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":60000},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[],\"settings\":{\"autoFitToDataBounds\":false,\"backgroundColor\":\"#ffffff\",\"customIcons\":[],\"disableInteractive\":false,\"disableTooltipControl\":false,\"hideToolbarOverlay\":false,\"hideLayerControl\":false,\"hideViewControl\":false,\"initialLocation\":\"LAST_SAVED_LOCATION\",\"fixedLocation\":{\"lat\":0,\"lon\":0,\"zoom\":2},\"browserLocation\":{\"zoom\":2},\"keydownScrollZoom\":false,\"maxZoom\":24,\"minZoom\":0,\"showScaleControl\":false,\"showSpatialFilters\":true,\"showTimesliderToggleButton\":true,\"spatialFiltersAlpa\":0.3,\"spatialFiltersFillColor\":\"#DA8B45\",\"spatialFiltersLineColor\":\"#DA8B45\"}}","title":"Map","uiStateJSON":"{\"isLayerTOCOpen\":true,\"openTOCDetails\":[]}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-24T18:22:40.360Z","id":"managed-d7ab-46eb-a807-8fed28ed8566","managed":true,"references":[],"type":"map","typeMigrationVersion":"8.4.0","updated_at":"2024-01-24T18:23:07.090Z","version":"WzEyLDFd"} +{"attributes":{"description":"","layerListJSON":"[{\"locale\":\"autoselect\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"isAutoSelect\":true,\"lightModeDefault\":\"road_map_desaturated\"},\"id\":\"5ff9c98e-e0d3-4aff-ac98-b33c191496b4\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"style\":{\"type\":\"EMS_VECTOR_TILE\",\"color\":\"\"},\"includeInFitToBounds\":true,\"type\":\"EMS_VECTOR_TILE\"}]","mapStateJSON":"{\"adHocDataViews\":[],\"zoom\":1.4,\"center\":{\"lon\":0,\"lat\":19.94277},\"timeFilters\":{\"from\":\"now-15m\",\"to\":\"now\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":60000},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[],\"settings\":{\"autoFitToDataBounds\":false,\"backgroundColor\":\"#ffffff\",\"customIcons\":[],\"disableInteractive\":false,\"disableTooltipControl\":false,\"hideToolbarOverlay\":false,\"hideLayerControl\":false,\"hideViewControl\":false,\"initialLocation\":\"LAST_SAVED_LOCATION\",\"fixedLocation\":{\"lat\":0,\"lon\":0,\"zoom\":2},\"browserLocation\":{\"zoom\":2},\"keydownScrollZoom\":false,\"maxZoom\":24,\"minZoom\":0,\"showScaleControl\":false,\"showSpatialFilters\":true,\"showTimesliderToggleButton\":true,\"spatialFiltersAlpa\":0.3,\"spatialFiltersFillColor\":\"#DA8B45\",\"spatialFiltersLineColor\":\"#DA8B45\"}}","title":"Managed map","uiStateJSON":"{\"isLayerTOCOpen\":true,\"openTOCDetails\":[]}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-24T18:22:40.360Z","id":"managed-d7ab-46eb-a807-8fed28ed8566","managed":true,"references":[],"type":"map","typeMigrationVersion":"8.4.0","updated_at":"2024-01-24T18:23:07.090Z","version":"WzEyLDFd"} -{"attributes":{"description":"","layerListJSON":"[{\"locale\":\"autoselect\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"isAutoSelect\":true,\"lightModeDefault\":\"road_map_desaturated\"},\"id\":\"5ff9c98e-e0d3-4aff-ac98-b33c191496b4\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"style\":{\"type\":\"EMS_VECTOR_TILE\",\"color\":\"\"},\"includeInFitToBounds\":true,\"type\":\"EMS_VECTOR_TILE\"}]","mapStateJSON":"{\"adHocDataViews\":[],\"zoom\":1.4,\"center\":{\"lon\":0,\"lat\":19.94277},\"timeFilters\":{\"from\":\"now-15m\",\"to\":\"now\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":60000},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[],\"settings\":{\"autoFitToDataBounds\":false,\"backgroundColor\":\"#ffffff\",\"customIcons\":[],\"disableInteractive\":false,\"disableTooltipControl\":false,\"hideToolbarOverlay\":false,\"hideLayerControl\":false,\"hideViewControl\":false,\"initialLocation\":\"LAST_SAVED_LOCATION\",\"fixedLocation\":{\"lat\":0,\"lon\":0,\"zoom\":2},\"browserLocation\":{\"zoom\":2},\"keydownScrollZoom\":false,\"maxZoom\":24,\"minZoom\":0,\"showScaleControl\":false,\"showSpatialFilters\":true,\"showTimesliderToggleButton\":true,\"spatialFiltersAlpa\":0.3,\"spatialFiltersFillColor\":\"#DA8B45\",\"spatialFiltersLineColor\":\"#DA8B45\"}}","title":"Map","uiStateJSON":"{\"isLayerTOCOpen\":true,\"openTOCDetails\":[]}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-24T18:22:40.360Z","id":"unmanaged-d7ab-46eb-a807-8fed28ed8566","managed":false,"references":[],"type":"map","typeMigrationVersion":"8.4.0","updated_at":"2024-01-24T18:23:07.090Z","version":"WzEyLDFd"} +{"attributes":{"description":"","layerListJSON":"[{\"locale\":\"autoselect\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"isAutoSelect\":true,\"lightModeDefault\":\"road_map_desaturated\"},\"id\":\"5ff9c98e-e0d3-4aff-ac98-b33c191496b4\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"style\":{\"type\":\"EMS_VECTOR_TILE\",\"color\":\"\"},\"includeInFitToBounds\":true,\"type\":\"EMS_VECTOR_TILE\"}]","mapStateJSON":"{\"adHocDataViews\":[],\"zoom\":1.4,\"center\":{\"lon\":0,\"lat\":19.94277},\"timeFilters\":{\"from\":\"now-15m\",\"to\":\"now\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":60000},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[],\"settings\":{\"autoFitToDataBounds\":false,\"backgroundColor\":\"#ffffff\",\"customIcons\":[],\"disableInteractive\":false,\"disableTooltipControl\":false,\"hideToolbarOverlay\":false,\"hideLayerControl\":false,\"hideViewControl\":false,\"initialLocation\":\"LAST_SAVED_LOCATION\",\"fixedLocation\":{\"lat\":0,\"lon\":0,\"zoom\":2},\"browserLocation\":{\"zoom\":2},\"keydownScrollZoom\":false,\"maxZoom\":24,\"minZoom\":0,\"showScaleControl\":false,\"showSpatialFilters\":true,\"showTimesliderToggleButton\":true,\"spatialFiltersAlpa\":0.3,\"spatialFiltersFillColor\":\"#DA8B45\",\"spatialFiltersLineColor\":\"#DA8B45\"}}","title":"Unmanaged map","uiStateJSON":"{\"isLayerTOCOpen\":true,\"openTOCDetails\":[]}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-24T18:22:40.360Z","id":"unmanaged-d7ab-46eb-a807-8fed28ed8566","managed":false,"references":[],"type":"map","typeMigrationVersion":"8.4.0","updated_at":"2024-01-24T18:23:07.090Z","version":"WzEyLDFd"} + +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"optionsJSON":"{\"useMargins\":true,\"syncColors\":false,\"syncCursor\":true,\"syncTooltips\":false,\"hidePanelTitles\":false}","panelsJSON":"[{\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"536c52e4-ecf1-4cde-9323-cc1c3de1fdd2\"},\"panelIndex\":\"536c52e4-ecf1-4cde-9323-cc1c3de1fdd2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_536c52e4-ecf1-4cde-9323-cc1c3de1fdd2\"},{\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"f19bd0df-03e7-4181-9adf-e3b3b4c97e19\"},\"panelIndex\":\"f19bd0df-03e7-4181-9adf-e3b3b4c97e19\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_f19bd0df-03e7-4181-9adf-e3b3b4c97e19\"},{\"type\":\"map\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"26f25c71-2b7c-4540-8c1c-c003b5657978\"},\"panelIndex\":\"26f25c71-2b7c-4540-8c1c-c003b5657978\",\"embeddableConfig\":{\"mapCenter\":{\"lat\":19.94277,\"lon\":0,\"zoom\":1.4},\"mapBuffer\":{\"minLon\":-180,\"minLat\":-66.51326,\"maxLon\":180,\"maxLat\":66.51326},\"isLayerTOCOpen\":true,\"openTOCDetails\":[],\"hiddenLayers\":[],\"enhancements\":{}},\"panelRefName\":\"panel_26f25c71-2b7c-4540-8c1c-c003b5657978\"},{\"type\":\"search\",\"gridData\":{\"x\":24,\"y\":15,\"w\":24,\"h\":15,\"i\":\"d7e33257-de57-40cb-9171-3dd739dd1875\"},\"panelIndex\":\"d7e33257-de57-40cb-9171-3dd739dd1875\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_d7e33257-de57-40cb-9171-3dd739dd1875\"}]","timeRestore":false,"title":"Managed dashboard with by-ref panels","version":1},"coreMigrationVersion":"8.8.0","created_at":"2024-01-31T21:57:49.102Z","id":"c44c86f9-b105-4a9c-9a24-449a58a827f3","managed":true,"references":[{"id":"managed-feb9-4ba6-9538-1b8f67fb4f57","name":"536c52e4-ecf1-4cde-9323-cc1c3de1fdd2:panel_536c52e4-ecf1-4cde-9323-cc1c3de1fdd2","type":"visualization"},{"id":"managed-36db-4a3b-a4ba-7a64ab8f130b","name":"f19bd0df-03e7-4181-9adf-e3b3b4c97e19:panel_f19bd0df-03e7-4181-9adf-e3b3b4c97e19","type":"lens"},{"id":"managed-d7ab-46eb-a807-8fed28ed8566","name":"26f25c71-2b7c-4540-8c1c-c003b5657978:panel_26f25c71-2b7c-4540-8c1c-c003b5657978","type":"map"},{"id":"managed-3d62-4113-ac7c-de2e20a68fbc","name":"d7e33257-de57-40cb-9171-3dd739dd1875:panel_d7e33257-de57-40cb-9171-3dd739dd1875","type":"search"}],"type":"dashboard","typeMigrationVersion":"8.9.0","updated_at":"2024-01-31T21:57:49.102Z","version":"WzEwOSwxXQ=="} diff --git a/test/functional/services/dashboard/add_panel.ts b/test/functional/services/dashboard/add_panel.ts index 65adf6dee5359..5b16c6af9ca20 100644 --- a/test/functional/services/dashboard/add_panel.ts +++ b/test/functional/services/dashboard/add_panel.ts @@ -230,22 +230,41 @@ export class DashboardAddPanelService extends FtrService { return this.addEmbeddable(vizName, 'Visualization'); } - async addEmbeddable(embeddableName: string, embeddableType: string) { + async addEmbeddable( + embeddableName: string, + embeddableType?: string, + closePanelWhenComplete: boolean = true + ) { this.log.debug( `DashboardAddPanel.addEmbeddable, name: ${embeddableName}, type: ${embeddableType}` ); await this.ensureAddPanelIsShowing(); - await this.savedObjectsFinder.toggleFilter(embeddableType); - await this.savedObjectsFinder.filterEmbeddableNames(`"${embeddableName.replace('-', ' ')}"`); + await this.savedObjectsFinder.filterEmbeddableNames( + `${embeddableType ? 'type:(' + embeddableType + ') ' : ''}"${embeddableName.replace( + '-', + ' ' + )}"` + ); await this.testSubjects.click(`savedObjectTitle${embeddableName.split(' ').join('-')}`); await this.testSubjects.exists('addObjectToDashboardSuccess'); - await this.closeAddPanel(); + if (closePanelWhenComplete) { + await this.closeAddPanel(); + } // close "Added successfully" toast await this.common.clearAllToasts(); return embeddableName; } + async addEmbeddables(embeddables: Array<{ name: string; type?: string }>) { + const addedEmbeddables: string[] = []; + for (const { name, type } of embeddables) { + addedEmbeddables.push(await this.addEmbeddable(name, type, false)); + } + await this.closeAddPanel(); + return addedEmbeddables; + } + async panelAddLinkExists(name: string) { this.log.debug(`DashboardAddPanel.panelAddLinkExists(${name})`); await this.ensureAddPanelIsShowing(); diff --git a/test/functional/services/dashboard/panel_settings.ts b/test/functional/services/dashboard/panel_settings.ts index 36bd01b1fad0d..11b817b176b6e 100644 --- a/test/functional/services/dashboard/panel_settings.ts +++ b/test/functional/services/dashboard/panel_settings.ts @@ -64,6 +64,10 @@ export function DashboardCustomizePanelProvider({ getService, getPageObject }: F ); } }); + + await retry.waitFor('superDatePickerToggleQuickMenuButton to be present', async () => { + return Boolean(await this.findDatePickerQuickMenuButton()); + }); } public async disableCustomTimeRange() { diff --git a/tsconfig.base.json b/tsconfig.base.json index 3c6be26411d88..bb1f7b7877026 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -642,6 +642,8 @@ "@kbn/data-view-field-editor-plugin/*": ["src/plugins/data_view_field_editor/*"], "@kbn/data-view-management-plugin": ["src/plugins/data_view_management"], "@kbn/data-view-management-plugin/*": ["src/plugins/data_view_management/*"], + "@kbn/data-view-utils": ["packages/kbn-data-view-utils"], + "@kbn/data-view-utils/*": ["packages/kbn-data-view-utils/*"], "@kbn/data-views-plugin": ["src/plugins/data_views"], "@kbn/data-views-plugin/*": ["src/plugins/data_views/*"], "@kbn/data-visualizer-plugin": ["x-pack/plugins/data_visualizer"], diff --git a/versions.json b/versions.json index 90d1a205d66d6..8a5f18231e404 100644 --- a/versions.json +++ b/versions.json @@ -8,13 +8,13 @@ "currentMinor": true }, { - "version": "8.12.1", + "version": "8.12.2", "branch": "8.12", "currentMajor": true, "previousMinor": true }, { - "version": "7.17.18", + "version": "7.17.19", "branch": "7.17", "previousMajor": true } diff --git a/x-pack/packages/kbn-alerting-state-types/src/lifecycle_state.ts b/x-pack/packages/kbn-alerting-state-types/src/lifecycle_state.ts index 8fa5cb111f6d9..f82791dd4e4ea 100644 --- a/x-pack/packages/kbn-alerting-state-types/src/lifecycle_state.ts +++ b/x-pack/packages/kbn-alerting-state-types/src/lifecycle_state.ts @@ -20,6 +20,8 @@ const trackedAlertStateRt = t.type({ // count of consecutive recovered alerts for flapping // will reset if the alert is active or if equal to the statusChangeThreshold stored in the rule settings pendingRecoveredCount: t.number, + // count of consecutive active alerts will reset if the alert is recovered + activeCount: t.number, }); export type TrackedLifecycleAlertState = t.TypeOf; diff --git a/x-pack/packages/kbn-alerting-state-types/src/task_state/v1/schema.ts b/x-pack/packages/kbn-alerting-state-types/src/task_state/v1/schema.ts index 247c0d7fc8d87..247cc7124c000 100644 --- a/x-pack/packages/kbn-alerting-state-types/src/task_state/v1/schema.ts +++ b/x-pack/packages/kbn-alerting-state-types/src/task_state/v1/schema.ts @@ -36,8 +36,7 @@ export const metaSchema = schema.object({ // will reset if the alert is active or if equal to the statusChangeThreshold stored in the rule settings pendingRecoveredCount: schema.maybe(schema.number()), uuid: schema.maybe(schema.string()), - // count of consecutive active alerts - // will reset if the alert is recovered or if equal to notificationDelay.active stored in the rule + // count of consecutive active alerts will reset if the alert is recovered activeCount: schema.maybe(schema.number()), }); diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/index_properties/index.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/index_properties/index.tsx index 3de5aba8bcc59..6e1819d35537d 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/index_properties/index.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/index_properties/index.tsx @@ -44,7 +44,7 @@ import { useAddToNewCase } from '../../use_add_to_new_case'; import { useMappings } from '../../use_mappings'; import { useUnallowedValues } from '../../use_unallowed_values'; import { useDataQualityContext } from '../data_quality_context'; -import { getSizeInBytes, postResult } from '../../helpers'; +import { formatStorageResult, postStorageResult, getSizeInBytes } from '../../helpers'; const EMPTY_MARKDOWN_COMMENTS: string[] = []; @@ -249,6 +249,8 @@ const IndexPropertiesComponent: React.FC = ({ }) : EMPTY_MARKDOWN_COMMENTS; + const checkedAt = partitionedFieldMetadata ? Date.now() : undefined; + const updatedRollup = { ...patternRollup, results: { @@ -262,13 +264,14 @@ const IndexPropertiesComponent: React.FC = ({ markdownComments, pattern, sameFamily: indexSameFamily, + checkedAt, }, }, }; updatePatternRollup(updatedRollup); if (indexId && requestTime != null && requestTime > 0 && partitionedFieldMetadata) { - const checkMetadata = { + const report = { batchId: uuidv4(), ecsVersion: EcsVersion, errorCount: error ? 1 : 0, @@ -294,10 +297,13 @@ const IndexPropertiesComponent: React.FC = ({ partitionedFieldMetadata.incompatible ), }; - telemetryEvents.reportDataQualityIndexChecked?.(checkMetadata); + telemetryEvents.reportDataQualityIndexChecked?.(report); - const result = { meta: checkMetadata, rollup: updatedRollup }; - postResult({ result, httpFetch, toasts, abortController: new AbortController() }); + const result = updatedRollup.results[indexName]; + if (result) { + const storageResult = formatStorageResult({ result, report, partitionedFieldMetadata }); + postStorageResult({ storageResult, httpFetch, toasts }); + } } } } diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/pattern/helpers.test.ts b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/pattern/helpers.test.ts index b2a7f6ef26a6e..cdf6251113751 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/pattern/helpers.test.ts +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/pattern/helpers.test.ts @@ -274,6 +274,7 @@ describe('helpers', () => { ], pattern: 'auditbeat-*', sameFamily: 0, + checkedAt: 1706526408000, }, }; const isILMAvailable = true; @@ -300,6 +301,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 4, sizeInBytes: 733175040, + checkedAt: undefined, }, { docsCount: 1628343, @@ -309,6 +311,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 4, sizeInBytes: 731583142, + checkedAt: undefined, }, { docsCount: 4, @@ -318,6 +321,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 4, sizeInBytes: 28413, + checkedAt: 1706526408000, }, ]); }); @@ -344,6 +348,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 4, sizeInBytes: 733175040, + checkedAt: undefined, }, { docsCount: 1628343, @@ -353,6 +358,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 4, sizeInBytes: 731583142, + checkedAt: undefined, }, { docsCount: 4, @@ -362,6 +368,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 4, sizeInBytes: 28413, + checkedAt: 1706526408000, }, ]); }); @@ -388,6 +395,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 4, sizeInBytes: 28413, + checkedAt: 1706526408000, }, { docsCount: 1628343, @@ -397,6 +405,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 4, sizeInBytes: 731583142, + checkedAt: undefined, }, { docsCount: 1630289, @@ -406,6 +415,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 4, sizeInBytes: 733175040, + checkedAt: undefined, }, ]); }); @@ -432,6 +442,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 4, sizeInBytes: 0, + checkedAt: undefined, }, { docsCount: 0, @@ -441,6 +452,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 4, sizeInBytes: 0, + checkedAt: undefined, }, { docsCount: 0, @@ -450,6 +462,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 4, sizeInBytes: 0, + checkedAt: undefined, }, ]); }); @@ -701,6 +714,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 43357342, + checkedAt: 1706526408000, }, { docsCount: 48068, @@ -710,6 +724,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 32460397, + checkedAt: 1706526408000, }, { docsCount: 48064, @@ -719,6 +734,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 42782794, + checkedAt: 1706526408000, }, { docsCount: 47868, @@ -728,6 +744,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 31575964, + checkedAt: 1706526408000, }, { docsCount: 47827, @@ -737,6 +754,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 44130657, + checkedAt: 1706526408000, }, { docsCount: 47642, @@ -746,6 +764,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 42412521, + checkedAt: 1706526408000, }, { docsCount: 47545, @@ -755,6 +774,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 41423244, + checkedAt: 1706526408000, }, { docsCount: 47531, @@ -764,6 +784,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 32394133, + checkedAt: 1706526408000, }, { docsCount: 47530, @@ -773,6 +794,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 43015519, + checkedAt: 1706526408000, }, { docsCount: 47520, @@ -782,6 +804,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 42230604, + checkedAt: 1706526408000, }, { docsCount: 47496, @@ -791,6 +814,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 41710968, + checkedAt: 1706526408000, }, { docsCount: 47486, @@ -800,6 +824,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 42295944, + checkedAt: 1706526408000, }, { docsCount: 47486, @@ -809,6 +834,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 41761321, + checkedAt: 1706526408000, }, { docsCount: 47460, @@ -818,6 +844,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 30481198, + checkedAt: 1706526408000, }, { docsCount: 47439, @@ -827,6 +854,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 41554041, + checkedAt: 1706526408000, }, { docsCount: 47395, @@ -836,6 +864,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 42815907, + checkedAt: 1706526408000, }, { docsCount: 47394, @@ -845,6 +874,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 41157112, + checkedAt: 1706526408000, }, { docsCount: 47372, @@ -854,6 +884,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 31626792, + checkedAt: 1706526408000, }, { docsCount: 47369, @@ -863,6 +894,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 41828969, + checkedAt: 1706526408000, }, { docsCount: 47348, @@ -872,6 +904,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 40010773, + checkedAt: 1706526408000, }, { docsCount: 47339, @@ -881,6 +914,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 43480570, + checkedAt: 1706526408000, }, { docsCount: 47325, @@ -890,6 +924,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 41822475, + checkedAt: 1706526408000, }, { docsCount: 47294, @@ -899,6 +934,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 43018490, + checkedAt: 1706526408000, }, { docsCount: 24276, @@ -908,6 +944,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 23579440, + checkedAt: 1706526408000, }, { docsCount: 4, @@ -917,6 +954,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 28409, + checkedAt: 1706526408000, }, { docsCount: 0, @@ -926,6 +964,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 1118155, sizeInBytes: 247, + checkedAt: 1706526408000, }, ], pageSize: 10, diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/pattern/helpers.ts b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/pattern/helpers.ts index 82fd312a947e5..1bddcd83e7a14 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/pattern/helpers.ts +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/pattern/helpers.ts @@ -172,6 +172,7 @@ export const getSummaryTableItems = ({ pattern, patternDocsCount, sizeInBytes: getSizeInBytes({ stats, indexName }), + checkedAt: results?.[indexName]?.checkedAt, })); return orderBy([sortByColumn], [sortByDirection], summaryTableItems); diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/summary_table/helpers.test.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/summary_table/helpers.test.tsx index 6ab874eeac5b6..cfcd23b86d2a1 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/summary_table/helpers.test.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/summary_table/helpers.test.tsx @@ -143,6 +143,7 @@ describe('helpers', () => { pattern: 'auditbeat-*', patternDocsCount: 57410, sizeInBytes: 103344068, + checkedAt: Date.now(), }; const hasIncompatible: IndexSummaryTableItem = { @@ -188,6 +189,7 @@ describe('helpers', () => { }, { field: 'ilmPhase', name: 'ILM Phase', sortable: true, truncateText: false }, { field: 'sizeInBytes', name: 'Size', sortable: true, truncateText: false }, + { field: 'checkedAt', name: 'Last check', sortable: true, truncateText: false }, ]); }); diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/summary_table/helpers.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/summary_table/helpers.tsx index f80678fff8cb2..55e26200f0e44 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/summary_table/helpers.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/summary_table/helpers.tsx @@ -5,8 +5,9 @@ * 2.0. */ -import type { EuiBasicTableColumn } from '@elastic/eui'; import { + EuiBasicTableColumn, + EuiText, EuiBadge, EuiButtonIcon, EuiIcon, @@ -17,6 +18,7 @@ import { RIGHT_ALIGNMENT, } from '@elastic/eui'; import React from 'react'; +import moment from 'moment'; import styled from 'styled-components'; import { EMPTY_STAT, getIlmPhaseDescription, getIncompatibleStatColor } from '../../helpers'; @@ -41,6 +43,7 @@ export interface IndexSummaryTableItem { pattern: string; patternDocsCount: number; sizeInBytes: number; + checkedAt: number | undefined; } export const getResultToolTip = (incompatible: number | undefined): string => { @@ -237,6 +240,17 @@ export const getSummaryTableColumns = ({ sortable: true, truncateText: false, }, + { + field: 'checkedAt', + name: i18n.LAST_CHECK, + render: (_, { checkedAt }) => ( + + {checkedAt && moment(checkedAt).isValid() ? moment(checkedAt).fromNow() : EMPTY_STAT} + + ), + sortable: true, + truncateText: false, + }, ]; export const getShowPagination = ({ diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/summary_table/translations.ts b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/summary_table/translations.ts index 0101708db9f9d..8d8e4adb9944f 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/summary_table/translations.ts +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/summary_table/translations.ts @@ -111,6 +111,13 @@ export const SIZE = i18n.translate( } ); +export const LAST_CHECK = i18n.translate( + 'securitySolutionPackages.ecsDataQualityDashboard.summaryTable.lastCheckColumn', + { + defaultMessage: 'Last check', + } +); + export const THIS_INDEX_HAS_NOT_BEEN_CHECKED = i18n.translate( 'securitySolutionPackages.ecsDataQualityDashboard.summaryTable.thisIndexHasNotBeenCheckedTooltip', { diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.test.ts b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.test.ts index a20c8144c5773..830d463f5fe57 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.test.ts +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.test.ts @@ -35,9 +35,9 @@ import { getTotalSizeInBytes, hasValidTimestampMapping, isMappingCompatible, - postResult, - getResults, - ResultData, + postStorageResult, + getStorageResults, + StorageResult, } from './helpers'; import { hostNameWithTextMapping, @@ -102,6 +102,7 @@ describe('helpers', () => { ], pattern: 'auditbeat-*', sameFamily: 0, + checkedAt: Date.now(), }; it('returns undefined when results is undefined', () => { @@ -1193,6 +1194,7 @@ describe('helpers', () => { markdownComments: ['foo', 'bar', 'baz'], pattern: 'packetbeat-*', sameFamily: 0, + checkedAt: Date.now(), }, '.ds-packetbeat-8.6.1-2023.02.04-000001': { docsCount: 1628343, @@ -1203,6 +1205,7 @@ describe('helpers', () => { markdownComments: ['foo', 'bar', 'baz'], pattern: 'packetbeat-*', sameFamily: 0, + checkedAt: Date.now(), }, }; @@ -1220,6 +1223,7 @@ describe('helpers', () => { markdownComments: ['foo', 'bar', 'baz'], pattern: 'auditbeat-*', sameFamily: 0, + checkedAt: Date.now(), }, 'auditbeat-custom-index-1': { docsCount: 4, @@ -1230,6 +1234,7 @@ describe('helpers', () => { markdownComments: ['foo', 'bar', 'baz'], pattern: 'auditbeat-*', sameFamily: 0, + checkedAt: Date.now(), }, 'auditbeat-custom-empty-index-1': { docsCount: 0, @@ -1240,6 +1245,7 @@ describe('helpers', () => { markdownComments: ['foo', 'bar', 'baz'], pattern: 'auditbeat-*', sameFamily: 0, + checkedAt: Date.now(), }, }; @@ -1257,6 +1263,7 @@ describe('helpers', () => { markdownComments: ['foo', 'bar', 'baz'], pattern: 'auditbeat-*', sameFamily: 0, + checkedAt: Date.now(), }, 'auditbeat-custom-index-1': { docsCount: 4, @@ -1267,6 +1274,7 @@ describe('helpers', () => { markdownComments: ['foo', 'bar', 'baz'], pattern: 'auditbeat-*', sameFamily: 0, + checkedAt: Date.now(), }, 'auditbeat-custom-empty-index-1': { docsCount: 0, @@ -1277,6 +1285,7 @@ describe('helpers', () => { markdownComments: ['foo', 'bar', 'baz'], pattern: 'auditbeat-*', sameFamily: 0, + checkedAt: Date.now(), }, }; @@ -1342,6 +1351,7 @@ describe('helpers', () => { markdownComments: ['foo', 'bar', 'baz'], pattern: 'packetbeat-*', sameFamily: 0, + checkedAt: Date.now(), }; expect(getErrorSummary(resultWithError)).toEqual({ @@ -1495,7 +1505,7 @@ describe('helpers', () => { }); }); - describe('postResult', () => { + describe('postStorageResult', () => { const { fetch } = httpServiceMock.createStartContract(); const { toasts } = notificationServiceMock.createStartContract(); beforeEach(() => { @@ -1503,10 +1513,10 @@ describe('helpers', () => { }); test('it posts the result', async () => { - const result = { meta: {}, rollup: {} } as unknown as ResultData; - await postResult({ + const storageResult = { indexName: 'test' } as unknown as StorageResult; + await postStorageResult({ + storageResult, httpFetch: fetch, - result, abortController: new AbortController(), toasts, }); @@ -1515,17 +1525,17 @@ describe('helpers', () => { '/internal/ecs_data_quality_dashboard/results', expect.objectContaining({ method: 'POST', - body: JSON.stringify(result), + body: JSON.stringify(storageResult), }) ); }); test('it throws error', async () => { - const result = { meta: {}, rollup: {} } as unknown as ResultData; + const storageResult = { indexName: 'test' } as unknown as StorageResult; fetch.mockRejectedValueOnce('test-error'); - await postResult({ + await postStorageResult({ httpFetch: fetch, - result, + storageResult, abortController: new AbortController(), toasts, }); @@ -1533,7 +1543,7 @@ describe('helpers', () => { }); }); - describe('getResults', () => { + describe('getStorageResults', () => { const { fetch } = httpServiceMock.createStartContract(); const { toasts } = notificationServiceMock.createStartContract(); beforeEach(() => { @@ -1541,10 +1551,10 @@ describe('helpers', () => { }); test('it gets the results', async () => { - await getResults({ + await getStorageResults({ httpFetch: fetch, abortController: new AbortController(), - patterns: ['auditbeat-*', 'packetbeat-*'], + pattern: 'auditbeat-*', toasts, }); @@ -1552,7 +1562,7 @@ describe('helpers', () => { '/internal/ecs_data_quality_dashboard/results', expect.objectContaining({ method: 'GET', - query: { patterns: 'auditbeat-*,packetbeat-*' }, + query: { pattern: 'auditbeat-*' }, }) ); }); @@ -1560,10 +1570,10 @@ describe('helpers', () => { it('should catch error', async () => { fetch.mockRejectedValueOnce('test-error'); - const results = await getResults({ + const results = await getStorageResults({ httpFetch: fetch, abortController: new AbortController(), - patterns: ['auditbeat-*', 'packetbeat-*'], + pattern: 'auditbeat-*', toasts, }); diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.ts b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.ts index a4d51233232e4..2107e7d3949da 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.ts +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.ts @@ -23,6 +23,7 @@ import type { EcsMetadata, EnrichedFieldMetadata, ErrorSummary, + IlmPhase, PartitionedFieldMetadata, PartitionedFieldMetadataStats, PatternRollup, @@ -449,51 +450,116 @@ export const getErrorSummaries = ( export const RESULTS_API_ROUTE = '/internal/ecs_data_quality_dashboard/results'; -export interface ResultData { - meta: DataQualityIndexCheckedParams; - rollup: PatternRollup; +export interface StorageResult { + batchId: string; + indexName: string; + isCheckAll: boolean; + checkedAt: number; + docsCount: number; + totalFieldCount: number; + ecsFieldCount: number; + customFieldCount: number; + incompatibleFieldCount: number; + sameFamilyFieldCount: number; + sameFamilyFields: string[]; + unallowedMappingFields: string[]; + unallowedValueFields: string[]; + sizeInBytes: number; + ilmPhase?: IlmPhase; + markdownComments: string[]; + ecsVersion: string; + indexId: string; + error: string | null; } -export async function postResult({ +export const formatStorageResult = ({ result, + report, + partitionedFieldMetadata, +}: { + result: DataQualityCheckResult; + report: DataQualityIndexCheckedParams; + partitionedFieldMetadata: PartitionedFieldMetadata; +}): StorageResult => ({ + batchId: report.batchId, + indexName: result.indexName, + isCheckAll: report.isCheckAll, + checkedAt: result.checkedAt ?? Date.now(), + docsCount: result.docsCount ?? 0, + totalFieldCount: partitionedFieldMetadata.all.length, + ecsFieldCount: partitionedFieldMetadata.ecsCompliant.length, + customFieldCount: partitionedFieldMetadata.custom.length, + incompatibleFieldCount: partitionedFieldMetadata.incompatible.length, + sameFamilyFieldCount: partitionedFieldMetadata.sameFamily.length, + sameFamilyFields: report.sameFamilyFields ?? [], + unallowedMappingFields: report.unallowedMappingFields ?? [], + unallowedValueFields: report.unallowedValueFields ?? [], + sizeInBytes: report.sizeInBytes ?? 0, + ilmPhase: result.ilmPhase, + markdownComments: result.markdownComments, + ecsVersion: report.ecsVersion, + indexId: report.indexId, + error: result.error, +}); + +export const formatResultFromStorage = ({ + storageResult, + pattern, +}: { + storageResult: StorageResult; + pattern: string; +}): DataQualityCheckResult => ({ + docsCount: storageResult.docsCount, + error: storageResult.error, + ilmPhase: storageResult.ilmPhase, + incompatible: storageResult.incompatibleFieldCount, + indexName: storageResult.indexName, + markdownComments: storageResult.markdownComments, + sameFamily: storageResult.sameFamilyFieldCount, + checkedAt: storageResult.checkedAt, + pattern, +}); + +export async function postStorageResult({ + storageResult, httpFetch, toasts, - abortController, + abortController = new AbortController(), }: { - result: ResultData; + storageResult: StorageResult; httpFetch: HttpHandler; toasts: IToasts; - abortController: AbortController; + abortController?: AbortController; }): Promise { try { await httpFetch(RESULTS_API_ROUTE, { method: 'POST', signal: abortController.signal, version: INTERNAL_API_VERSION, - body: JSON.stringify(result), + body: JSON.stringify(storageResult), }); } catch (err) { toasts.addError(err, { title: i18n.POST_RESULT_ERROR_TITLE }); } } -export async function getResults({ - patterns, +export async function getStorageResults({ + pattern, httpFetch, toasts, abortController, }: { - patterns: string[]; + pattern: string; httpFetch: HttpHandler; toasts: IToasts; abortController: AbortController; -}): Promise { +}): Promise { try { - const results = await httpFetch(RESULTS_API_ROUTE, { + const results = await httpFetch(RESULTS_API_ROUTE, { method: 'GET', signal: abortController.signal, version: INTERNAL_API_VERSION, - query: { patterns: patterns.join(',') }, + query: { pattern }, }); return results; } catch (err) { diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/mock/data_quality_check_result/mock_index.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/mock/data_quality_check_result/mock_index.tsx index f179a82c232e9..7f61f91b87212 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/mock/data_quality_check_result/mock_index.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/mock/data_quality_check_result/mock_index.tsx @@ -23,6 +23,7 @@ export const mockDataQualityCheckResult: Record ], pattern: 'auditbeat-*', sameFamily: 0, + checkedAt: 1706526408000, }, 'auditbeat-7.9.3-2023.02.13-000001': { docsCount: 2438, @@ -39,5 +40,6 @@ export const mockDataQualityCheckResult: Record ], pattern: 'auditbeat-*', sameFamily: 0, + checkedAt: 1706526408000, }, }; diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/mock/pattern_rollup/mock_alerts_pattern_rollup.ts b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/mock/pattern_rollup/mock_alerts_pattern_rollup.ts index f121621ff1ac1..513d034f52263 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/mock/pattern_rollup/mock_alerts_pattern_rollup.ts +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/mock/pattern_rollup/mock_alerts_pattern_rollup.ts @@ -83,6 +83,7 @@ export const alertIndexWithAllResults: PatternRollup = { markdownComments: ['foo', 'bar', 'baz'], pattern: '.alerts-security.alerts-default', sameFamily: 0, + checkedAt: 1706526408000, }, }, sizeInBytes: 29717961631, diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/mock/pattern_rollup/mock_auditbeat_pattern_rollup.ts b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/mock/pattern_rollup/mock_auditbeat_pattern_rollup.ts index 2f83f899dc0d2..15bb39a01714f 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/mock/pattern_rollup/mock_auditbeat_pattern_rollup.ts +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/mock/pattern_rollup/mock_auditbeat_pattern_rollup.ts @@ -155,6 +155,7 @@ export const auditbeatWithAllResults: PatternRollup = { markdownComments: ['foo', 'bar', 'baz'], pattern: 'auditbeat-*', sameFamily: 0, + checkedAt: 1706526408000, }, 'auditbeat-custom-index-1': { docsCount: 4, @@ -165,6 +166,7 @@ export const auditbeatWithAllResults: PatternRollup = { markdownComments: ['foo', 'bar', 'baz'], pattern: 'auditbeat-*', sameFamily: 0, + checkedAt: 1706526408000, }, 'auditbeat-custom-empty-index-1': { docsCount: 0, @@ -175,6 +177,7 @@ export const auditbeatWithAllResults: PatternRollup = { markdownComments: ['foo', 'bar', 'baz'], pattern: 'auditbeat-*', sameFamily: 0, + checkedAt: 1706526408000, }, }, sizeInBytes: 18820446, diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/mock/pattern_rollup/mock_packetbeat_pattern_rollup.ts b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/mock/pattern_rollup/mock_packetbeat_pattern_rollup.ts index 369803a44a3dd..339f1993e292d 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/mock/pattern_rollup/mock_packetbeat_pattern_rollup.ts +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/mock/pattern_rollup/mock_packetbeat_pattern_rollup.ts @@ -132,6 +132,7 @@ export const packetbeatWithSomeErrors: PatternRollup = { markdownComments: ['foo', 'bar', 'baz'], pattern: 'packetbeat-*', sameFamily: undefined, + checkedAt: 1706526408000, }, '.ds-packetbeat-8.6.1-2023.02.04-000001': { docsCount: 1628343, @@ -142,6 +143,7 @@ export const packetbeatWithSomeErrors: PatternRollup = { markdownComments: ['foo', 'bar', 'baz'], pattern: 'packetbeat-*', sameFamily: 0, + checkedAt: 1706526408000, }, }, sizeInBytes: 1096520898, diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/types.ts b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/types.ts index 9f507992d1509..f462777e1fc0b 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/types.ts +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/types.ts @@ -107,6 +107,7 @@ export interface DataQualityCheckResult { markdownComments: string[]; sameFamily: number | undefined; pattern: string; + checkedAt: number | undefined; } export interface PatternRollup { @@ -186,8 +187,8 @@ export type DataQualityIndexCheckedParams = DataQualityCheckAllCompletedParams & export interface DataQualityCheckAllCompletedParams { batchId: string; - ecsVersion?: string; - isCheckAll?: boolean; + ecsVersion: string; + isCheckAll: boolean; numberOfDocuments?: number; numberOfIncompatibleFields?: number; numberOfIndices?: number; diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/use_results_rollup/helpers.test.ts b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/use_results_rollup/helpers.test.ts index b37d0aecc25cf..b948b9584f996 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/use_results_rollup/helpers.test.ts +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/use_results_rollup/helpers.test.ts @@ -61,6 +61,7 @@ describe('helpers', () => { markdownComments: ['foo', 'bar', 'baz'], pattern: '.alerts-security.alerts-default', sameFamily: 7, + checkedAt: 1706526408000, }; const alertIndexWithSameFamily: PatternRollup = { @@ -260,6 +261,7 @@ describe('helpers', () => { ], pattern: 'packetbeat-*', sameFamily: 0, + checkedAt: expect.any(Number), }, }, sizeInBytes: 1464758182, @@ -361,6 +363,7 @@ describe('helpers', () => { ], pattern: 'packetbeat-*', sameFamily: 0, + checkedAt: expect.any(Number), }, }, sizeInBytes: 1464758182, @@ -450,6 +453,7 @@ describe('helpers', () => { indexName: '.ds-packetbeat-8.6.1-2023.02.04-000001', markdownComments: [], pattern: 'packetbeat-*', + checkedAt: undefined, }, }, sizeInBytes: 1464758182, @@ -510,6 +514,7 @@ describe('helpers', () => { ], pattern: 'packetbeat-*', sameFamily: 0, + checkedAt: expect.any(Number), }, }, sizeInBytes: 1464758182, diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/use_results_rollup/helpers.ts b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/use_results_rollup/helpers.ts index cca8ac331aa88..07f51572b6ba2 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/use_results_rollup/helpers.ts +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/use_results_rollup/helpers.ts @@ -140,6 +140,7 @@ export const updateResultOnCheckCompleted = ({ const incompatible = partitionedFieldMetadata?.incompatible.length; const sameFamily = partitionedFieldMetadata?.sameFamily.length; + const checkedAt = partitionedFieldMetadata ? Date.now() : undefined; return { ...patternRollups, @@ -156,6 +157,7 @@ export const updateResultOnCheckCompleted = ({ markdownComments, pattern, sameFamily, + checkedAt, }, }, }, diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/use_results_rollup/index.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/use_results_rollup/index.tsx index 27810fedffde4..907ffca5a45f6 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/use_results_rollup/index.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/use_results_rollup/index.tsx @@ -19,14 +19,16 @@ import { updateResultOnCheckCompleted, } from './helpers'; -import type { OnCheckCompleted, PatternRollup } from '../types'; +import type { DataQualityCheckResult, OnCheckCompleted, PatternRollup } from '../types'; import { getDocsCount, getIndexId, - getResults, + getStorageResults, getSizeInBytes, getTotalPatternSameFamily, - postResult, + postStorageResult, + formatStorageResult, + formatResultFromStorage, } from '../helpers'; import { getIlmPhase, getIndexIncompatible } from '../data_quality_panel/pattern/helpers'; import { useDataQualityContext } from '../data_quality_panel/data_quality_context'; @@ -60,9 +62,11 @@ interface UseResultsRollup { updatePatternRollup: (patternRollup: PatternRollup) => void; } -const useStoredPatternRollups = (patterns: string[]) => { +const useStoredPatternResults = (patterns: string[]) => { const { httpFetch, toasts } = useDataQualityContext(); - const [storedRollups, setStoredRollups] = useState>({}); + const [storedPatternResults, setStoredPatternResults] = useState< + Array<{ pattern: string; results: Record }> + >([]); useEffect(() => { if (isEmpty(patterns)) { @@ -71,20 +75,31 @@ const useStoredPatternRollups = (patterns: string[]) => { let ignore = false; const abortController = new AbortController(); - const fetchStoredRollups = async () => { - const results = await getResults({ httpFetch, abortController, patterns, toasts }); - if (results?.length && !ignore) { - setStoredRollups(Object.fromEntries(results.map(({ rollup }) => [rollup.pattern, rollup]))); + 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) { + setStoredPatternResults(patternResults); } }; - fetchStoredRollups(); + fetchStoredPatternResults(); return () => { ignore = true; }; }, [httpFetch, patterns, toasts]); - return storedRollups; + return storedPatternResults; }; export const useResultsRollup = ({ ilmPhases, patterns }: Props): UseResultsRollup => { @@ -92,28 +107,36 @@ export const useResultsRollup = ({ ilmPhases, patterns }: Props): UseResultsRoll const [patternIndexNames, setPatternIndexNames] = useState>({}); const [patternRollups, setPatternRollups] = useState>({}); - const storedPatternsRollups = useStoredPatternRollups(patterns); + const storedPatternsResults = useStoredPatternResults(patterns); useEffect(() => { - if (!isEmpty(storedPatternsRollups)) { - setPatternRollups((current) => ({ ...current, ...storedPatternsRollups })); + if (!isEmpty(storedPatternsResults)) { + setPatternRollups((current) => + storedPatternsResults.reduce( + (acc, { pattern, results }) => ({ + ...acc, + [pattern]: { + ...current[pattern], + pattern, + results, + }, + }), + current + ) + ); } - }, [storedPatternsRollups]); - - const updatePatternRollups = useCallback( - (updateRollups: (current: Record) => Record) => { - setPatternRollups((current) => updateRollups(current)); - }, - [] - ); + }, [storedPatternsResults]); const { telemetryEvents, isILMAvailable } = useDataQualityContext(); - const updatePatternRollup = useCallback( - (patternRollup: PatternRollup) => { - updatePatternRollups((current) => ({ ...current, [patternRollup.pattern]: patternRollup })); - }, - [updatePatternRollups] - ); + const updatePatternRollup = useCallback((patternRollup: PatternRollup) => { + setPatternRollups((current) => ({ + ...current, + [patternRollup.pattern]: { + ...patternRollup, + results: patternRollup.results ?? current[patternRollup.pattern]?.results, // prevent undefined results override existing + }, + })); + }, []); const totalDocsCount = useMemo(() => getTotalDocsCount(patternRollups), [patternRollups]); const totalIncompatible = useMemo(() => getTotalIncompatible(patternRollups), [patternRollups]); @@ -170,7 +193,7 @@ export const useResultsRollup = ({ ilmPhases, patterns }: Props): UseResultsRoll requestTime > 0 && partitionedFieldMetadata ) { - const metadata = { + const report = { batchId, ecsVersion: EcsVersion, errorCount: error ? 1 : 0, @@ -199,10 +222,13 @@ export const useResultsRollup = ({ ilmPhases, patterns }: Props): UseResultsRoll partitionedFieldMetadata.incompatible ), }; - telemetryEvents.reportDataQualityIndexChecked?.(metadata); + telemetryEvents.reportDataQualityIndexChecked?.(report); - const result = { meta: metadata, rollup: updatedRollup }; - postResult({ result, httpFetch, toasts, abortController: new AbortController() }); + const result = results[indexName]; + if (result) { + const storageResult = formatStorageResult({ result, report, partitionedFieldMetadata }); + postStorageResult({ storageResult, httpFetch, toasts }); + } } if (isLastCheck) { diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/kibana.jsonc b/x-pack/packages/security-solution/ecs_data_quality_dashboard/kibana.jsonc index a001420fade88..30cc9cf249820 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/kibana.jsonc +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/kibana.jsonc @@ -1,5 +1,5 @@ { "type": "shared-browser", "id": "@kbn/ecs-data-quality-dashboard", - "owner": "@elastic/security-threat-hunting-investigations" + "owner": "@elastic/security-threat-hunting-explore" } 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 daac98f67f750..1ed80f54be2a6 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 @@ -15,7 +15,6 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import type { DataView } from '@kbn/data-views-plugin/public'; -import type { Dictionary } from '@kbn/ml-url-state'; import { LOG_RATE_ANALYSIS_TYPE, type LogRateAnalysisType, @@ -62,7 +61,6 @@ export function getDocumentCountStatsSplitLabel( export interface LogRateAnalysisContentProps { /** The data view to analyze. */ dataView: DataView; - setGlobalState?: (params: Dictionary) => void; /** Timestamp for the start of the range for initial analysis */ initialAnalysisStart?: number | WindowParameters; timeRange?: { min: Moment; max: Moment }; @@ -84,7 +82,6 @@ export interface LogRateAnalysisContentProps { export const LogRateAnalysisContent: FC = ({ dataView, - setGlobalState, initialAnalysisStart: incomingInitialAnalysisStart, timeRange, esSearchQuery = DEFAULT_SEARCH_QUERY, @@ -150,7 +147,7 @@ export const LogRateAnalysisContent: FC = ({ dataView, 'log_rate_analysis', searchQuery, - setGlobalState, + undefined, currentSelectedSignificantItem, currentSelectedGroup, undefined, diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content_wrapper.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content_wrapper.tsx index 62fac2312ebfd..42ae9b2cf0868 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content_wrapper.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content_wrapper.tsx @@ -40,8 +40,6 @@ export interface LogRateAnalysisContentWrapperProps { stickyHistogram?: boolean; /** App dependencies */ appDependencies: AiopsAppDependencies; - /** On global timefilter update */ - setGlobalState?: any; /** Timestamp for start of initial analysis */ initialAnalysisStart?: number | WindowParameters; /** Optional time range */ @@ -66,7 +64,6 @@ export interface LogRateAnalysisContentWrapperProps { export const LogRateAnalysisContentWrapper: FC = ({ dataView, appDependencies, - setGlobalState, initialAnalysisStart, timeRange, esSearchQuery, @@ -100,7 +97,6 @@ export const LogRateAnalysisContentWrapper: FC = ({ stickyHistogram }) => { embeddingOrigin={AIOPS_TELEMETRY_ID.AIOPS_DEFAULT_SOURCE} esSearchQuery={searchQuery} onWindowParametersChange={onWindowParametersHandler} - setGlobalState={setGlobalState} stickyHistogram={stickyHistogram} /> diff --git a/x-pack/plugins/aiops/public/hooks/use_data.ts b/x-pack/plugins/aiops/public/hooks/use_data.ts index 4f0e2526d7a1d..36cfdbca0f52f 100644 --- a/x-pack/plugins/aiops/public/hooks/use_data.ts +++ b/x-pack/plugins/aiops/public/hooks/use_data.ts @@ -57,7 +57,7 @@ export const useData = ( const timeRangeMemoized = useMemo( () => timefilter.getActiveBounds(), // eslint-disable-next-line react-hooks/exhaustive-deps - [JSON.stringify(timefilter.getActiveBounds())] + [lastRefresh, JSON.stringify(timefilter.getTime())] ); const fieldStatsRequest: DocumentStatsSearchStrategyParams | undefined = useMemo(() => { diff --git a/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts b/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts index c154640acd4e8..6acbb0855d253 100644 --- a/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts +++ b/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts @@ -77,13 +77,13 @@ export function useDocumentCountStats>({}); + const cacheKey = stringHash( + `${JSON.stringify(searchParams)}_${JSON.stringify(searchParamsCompare)}` + ); + const fetchDocumentCountData = useCallback(async () => { if (!searchParams) return; - const cacheKey = stringHash( - `${JSON.stringify(searchParams)}_${JSON.stringify(searchParamsCompare)}` - ); - if (documentStatsCache[cacheKey]) { setDocumentStats(documentStatsCache[cacheKey]); return; @@ -172,7 +172,9 @@ export function useDocumentCountStats { schedule: CreateBodySchema['schedule']; actions: CreateBodySchema['actions']; notify_when?: CreateBodySchema['notify_when']; - notification_delay?: CreateBodySchema['notification_delay']; + alert_delay?: CreateBodySchema['alert_delay']; } export interface CreateRuleResponse { diff --git a/x-pack/plugins/alerting/common/routes/rule/response/index.ts b/x-pack/plugins/alerting/common/routes/rule/response/index.ts index 8e405c599e483..8c784e744d473 100644 --- a/x-pack/plugins/alerting/common/routes/rule/response/index.ts +++ b/x-pack/plugins/alerting/common/routes/rule/response/index.ts @@ -37,7 +37,7 @@ export { ruleSnoozeScheduleSchema as ruleSnoozeScheduleSchemaV1, notifyWhenSchema as notifyWhenSchemaV1, scheduleIdsSchema as scheduleIdsSchemaV1, - notificationDelaySchema as notificationDelaySchemaV1, + alertDelaySchema as alertDelaySchemaV1, } from './schemas/v1'; export type { diff --git a/x-pack/plugins/alerting/common/routes/rule/response/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/rule/response/schemas/v1.ts index 67f57926d5460..f485aa7374d6c 100644 --- a/x-pack/plugins/alerting/common/routes/rule/response/schemas/v1.ts +++ b/x-pack/plugins/alerting/common/routes/rule/response/schemas/v1.ts @@ -182,7 +182,7 @@ export const ruleSnoozeScheduleSchema = schema.object({ skipRecurrences: schema.maybe(schema.arrayOf(schema.string())), }); -export const notificationDelaySchema = schema.object({ +export const alertDelaySchema = schema.object({ active: schema.number(), }); @@ -218,7 +218,7 @@ export const ruleResponseSchema = schema.object({ revision: schema.number(), running: schema.maybe(schema.nullable(schema.boolean())), view_in_app_relative_url: schema.maybe(schema.nullable(schema.string())), - notification_delay: schema.maybe(notificationDelaySchema), + alert_delay: schema.maybe(alertDelaySchema), }); export const scheduleIdsSchema = schema.maybe(schema.arrayOf(schema.string())); diff --git a/x-pack/plugins/alerting/common/routes/rule/response/types/v1.ts b/x-pack/plugins/alerting/common/routes/rule/response/types/v1.ts index 453d1a96d24dc..25e08ab407ebb 100644 --- a/x-pack/plugins/alerting/common/routes/rule/response/types/v1.ts +++ b/x-pack/plugins/alerting/common/routes/rule/response/types/v1.ts @@ -53,5 +53,5 @@ export interface RuleResponse { revision: RuleResponseSchemaType['revision']; running?: RuleResponseSchemaType['running']; view_in_app_relative_url?: RuleResponseSchemaType['view_in_app_relative_url']; - notification_delay?: RuleResponseSchemaType['notification_delay']; + alert_delay?: RuleResponseSchemaType['alert_delay']; } diff --git a/x-pack/plugins/alerting/common/rule.ts b/x-pack/plugins/alerting/common/rule.ts index 590c1d4312d57..7cec5bdbdd7a6 100644 --- a/x-pack/plugins/alerting/common/rule.ts +++ b/x-pack/plugins/alerting/common/rule.ts @@ -141,7 +141,7 @@ export interface MappedParamsProperties { export type MappedParams = SavedObjectAttributes & MappedParamsProperties; -export interface NotificationDelay { +export interface AlertDelay extends SavedObjectAttributes { active: number; } @@ -178,7 +178,7 @@ export interface Rule { revision: number; running?: boolean | null; viewInAppRelativeUrl?: string; - notificationDelay?: NotificationDelay; + alertDelay?: AlertDelay; } export interface SanitizedAlertsFilter extends AlertsFilter { @@ -222,6 +222,7 @@ export type SanitizedRuleConfig = Pick< | 'muteAll' | 'revision' | 'snoozeSchedule' + | 'alertDelay' > & { producer: string; ruleTypeId: string; diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts index 0ef8f7c31e019..81848c3e65686 100644 --- a/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts @@ -308,6 +308,7 @@ describe('Alerts Client', () => { flappingSettings: DEFAULT_FLAPPING_SETTINGS, notifyOnActionGroupChange: true, maintenanceWindowIds: [], + alertDelay: 0, }; }); @@ -516,6 +517,25 @@ describe('Alerts Client', () => { }); }); + test('should not index new alerts if the activeCount is less than the rule alertDelay', async () => { + const alertsClient = new AlertsClient<{}, {}, {}, 'default', 'recovered'>({ + ...alertsClientParams, + rule: { ...alertsClientParams.rule, alertDelay: 3 }, + }); + + await alertsClient.initializeExecution(defaultExecutionOpts); + + // Report 1 new alerts + const alertExecutorService = alertsClient.factory(); + alertExecutorService.create('1').scheduleActions('default'); + + alertsClient.processAndLogAlerts(processAndLogAlertsOpts); + + await alertsClient.persistAlerts(); + + expect(clusterClient.bulk).not.toHaveBeenCalled(); + }); + test('should update ongoing alerts in existing index', async () => { clusterClient.search.mockResolvedValue({ took: 10, diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts index cc443975cee46..0c1eb897a6e59 100644 --- a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts @@ -344,6 +344,11 @@ export class AlertsClient< }) ); } else { + // skip writing the alert document if the number of consecutive + // active alerts is less than the rule alertDelay threshold + if (activeAlerts[id].getActiveCount() < this.options.rule.alertDelay) { + continue; + } activeAlertsToIndex.push( buildNewAlert< AlertData, diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client_fixtures.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client_fixtures.ts index 90c2f4c40b65b..0da20a5e49b70 100644 --- a/x-pack/plugins/alerting/server/alerts_client/alerts_client_fixtures.ts +++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client_fixtures.ts @@ -25,6 +25,7 @@ export const alertRuleData: AlertRuleData = { revision: 0, spaceId: 'default', tags: ['rule-', '-tags'], + alertDelay: 0, }; export const mockAAD = { diff --git a/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts b/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts index 446ecfd79c8b8..d01060820ae52 100644 --- a/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts @@ -245,6 +245,7 @@ describe('Legacy Alerts Client', () => { flappingSettings: DEFAULT_FLAPPING_SETTINGS, notifyOnActionGroupChange: true, maintenanceWindowIds: ['window-id1', 'window-id2'], + alertDelay: 5, }); expect(processAlerts).toHaveBeenCalledWith({ @@ -275,13 +276,15 @@ describe('Legacy Alerts Client', () => { }, true, 'default', + 5, {}, { '1': new Alert('1', testAlert1), '2': new Alert('2', testAlert2), }, {}, - {} + {}, + null ); expect(logAlerts).toHaveBeenCalledWith({ diff --git a/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.ts index 0de97a8a29be6..a5cfc642a19ee 100644 --- a/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.ts +++ b/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.ts @@ -140,6 +140,7 @@ export class LegacyAlertsClient< notifyOnActionGroupChange, flappingSettings, maintenanceWindowIds, + alertDelay, }: ProcessAlertsOpts) { const { newAlerts: processedAlertsNew, @@ -168,10 +169,12 @@ export class LegacyAlertsClient< flappingSettings, notifyOnActionGroupChange, this.options.ruleType.defaultActionGroupId, + alertDelay, processedAlertsNew, processedAlertsActive, trimmedAlertsRecovered, - processedAlertsRecoveredCurrent + processedAlertsRecoveredCurrent, + this.startedAtString ); alerts.currentRecoveredAlerts = merge(alerts.currentRecoveredAlerts, earlyRecoveredAlerts); @@ -203,11 +206,13 @@ export class LegacyAlertsClient< flappingSettings, notifyOnActionGroupChange, maintenanceWindowIds, + alertDelay, }: ProcessAndLogAlertsOpts) { this.processAlerts({ notifyOnActionGroupChange, flappingSettings, maintenanceWindowIds, + alertDelay, }); this.logAlerts({ diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/format_rule.test.ts b/x-pack/plugins/alerting/server/alerts_client/lib/format_rule.test.ts index 9f335f8266b21..c9cc7fbefbfa4 100644 --- a/x-pack/plugins/alerting/server/alerts_client/lib/format_rule.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/lib/format_rule.test.ts @@ -61,6 +61,7 @@ describe('formatRule', () => { revision: 0, spaceId: 'default', tags: ['rule-', '-tags'], + alertDelay: 0, }, ruleType, }) diff --git a/x-pack/plugins/alerting/server/alerts_client/types.ts b/x-pack/plugins/alerting/server/alerts_client/types.ts index 44690d5881179..1de1c06f5e826 100644 --- a/x-pack/plugins/alerting/server/alerts_client/types.ts +++ b/x-pack/plugins/alerting/server/alerts_client/types.ts @@ -47,6 +47,7 @@ export interface AlertRuleData { revision: number; spaceId: string; tags: string[]; + alertDelay: number; } export interface AlertRule { @@ -111,12 +112,14 @@ export interface ProcessAndLogAlertsOpts { flappingSettings: RulesSettingsFlappingProperties; notifyOnActionGroupChange: boolean; maintenanceWindowIds: string[]; + alertDelay: number; } export interface ProcessAlertsOpts { flappingSettings: RulesSettingsFlappingProperties; notifyOnActionGroupChange: boolean; maintenanceWindowIds: string[]; + alertDelay: number; } export interface LogAlertsOpts { diff --git a/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts b/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts index b73f671ab0695..def19570f7e0a 100644 --- a/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts +++ b/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts @@ -1475,6 +1475,7 @@ describe('Alerts Service', () => { revision: 0, spaceId: 'default', tags: ['rule-', '-tags'], + alertDelay: 0, }, }); @@ -1495,6 +1496,7 @@ describe('Alerts Service', () => { revision: 0, spaceId: 'default', tags: ['rule-', '-tags'], + alertDelay: 0, }, kibanaVersion: '8.8.0', }); @@ -1528,6 +1530,7 @@ describe('Alerts Service', () => { revision: 0, spaceId: 'default', tags: ['rule-', '-tags'], + alertDelay: 0, }, }); @@ -1576,6 +1579,7 @@ describe('Alerts Service', () => { revision: 0, spaceId: 'default', tags: ['rule-', '-tags'], + alertDelay: 0, }, }); @@ -1610,6 +1614,7 @@ describe('Alerts Service', () => { revision: 0, spaceId: 'default', tags: ['rule-', '-tags'], + alertDelay: 0, }, kibanaVersion: '8.8.0', }); @@ -1674,6 +1679,7 @@ describe('Alerts Service', () => { revision: 0, spaceId: 'default', tags: ['rule-', '-tags'], + alertDelay: 0, }, }), alertsService.createAlertsClient({ @@ -1691,6 +1697,7 @@ describe('Alerts Service', () => { revision: 0, spaceId: 'default', tags: ['rule-', '-tags'], + alertDelay: 0, }, }), ]); @@ -1725,6 +1732,7 @@ describe('Alerts Service', () => { revision: 0, spaceId: 'default', tags: ['rule-', '-tags'], + alertDelay: 0, }, kibanaVersion: '8.8.0', }); @@ -1781,6 +1789,7 @@ describe('Alerts Service', () => { revision: 0, spaceId: 'default', tags: ['rule-', '-tags'], + alertDelay: 0, }, }); @@ -1801,6 +1810,7 @@ describe('Alerts Service', () => { revision: 0, spaceId: 'default', tags: ['rule-', '-tags'], + alertDelay: 0, }, kibanaVersion: '8.8.0', }); @@ -1865,6 +1875,7 @@ describe('Alerts Service', () => { revision: 0, spaceId: 'default', tags: ['rule-', '-tags'], + alertDelay: 0, }, }); }; @@ -1892,6 +1903,7 @@ describe('Alerts Service', () => { revision: 0, spaceId: 'default', tags: ['rule-', '-tags'], + alertDelay: 0, }, kibanaVersion: '8.8.0', }); @@ -1961,6 +1973,7 @@ describe('Alerts Service', () => { revision: 0, spaceId: 'default', tags: ['rule-', '-tags'], + alertDelay: 0, }, }); }; @@ -2026,6 +2039,7 @@ describe('Alerts Service', () => { revision: 0, spaceId: 'default', tags: ['rule-', '-tags'], + alertDelay: 0, }, }); @@ -2091,6 +2105,7 @@ describe('Alerts Service', () => { revision: 0, spaceId: 'default', tags: ['rule-', '-tags'], + alertDelay: 0, }, }); @@ -2154,6 +2169,7 @@ describe('Alerts Service', () => { revision: 0, spaceId: 'default', tags: ['rule-', '-tags'], + alertDelay: 0, }, }); diff --git a/x-pack/plugins/alerting/server/alerts_service/lib/set_alerts_to_untracked.ts b/x-pack/plugins/alerting/server/alerts_service/lib/set_alerts_to_untracked.ts index 4b1e67baaba91..d0e02fb8b1783 100644 --- a/x-pack/plugins/alerting/server/alerts_service/lib/set_alerts_to_untracked.ts +++ b/x-pack/plugins/alerting/server/alerts_service/lib/set_alerts_to_untracked.ts @@ -18,6 +18,7 @@ import { ALERT_STATUS_UNTRACKED, ALERT_TIME_RANGE, ALERT_UUID, + AlertStatus, } from '@kbn/rule-data-utils'; export interface SetAlertsToUntrackedOpts { @@ -39,20 +40,15 @@ interface ConsumersAndRuleTypesAggregation { }; } -export async function setAlertsToUntracked({ - logger, - esClient, - indices, +const getQuery = ({ ruleIds = [], - alertUuids = [], // OPTIONAL - If no alertUuids are passed, untrack ALL ids by default, - ensureAuthorized, + alertUuids = [], + alertStatus, }: { - logger: Logger; - esClient: ElasticsearchClient; -} & SetAlertsToUntrackedOpts): Promise { - if (isEmpty(ruleIds) && isEmpty(alertUuids)) - throw new Error('Must provide either ruleIds or alertUuids'); - + ruleIds?: string[]; + alertUuids?: string[]; + alertStatus: AlertStatus; +}) => { const shouldMatchRules: Array<{ term: Record }> = ruleIds.map( (ruleId) => ({ term: { @@ -71,12 +67,12 @@ export async function setAlertsToUntracked({ const statusTerms: Array<{ term: Record }> = [ { term: { - [ALERT_STATUS]: { value: ALERT_STATUS_ACTIVE }, + [ALERT_STATUS]: { value: alertStatus }, }, }, ]; - const must = [ + return [ ...statusTerms, { bool: { @@ -90,6 +86,21 @@ export async function setAlertsToUntracked({ }, }, ]; +}; + +export async function setAlertsToUntracked({ + logger, + esClient, + indices, + ruleIds = [], + alertUuids = [], // OPTIONAL - If no alertUuids are passed, untrack ALL ids by default, + ensureAuthorized, +}: { + logger: Logger; + esClient: ElasticsearchClient; +} & SetAlertsToUntrackedOpts): Promise { + if (isEmpty(ruleIds) && isEmpty(alertUuids)) + throw new Error('Must provide either ruleIds or alertUuids'); if (ensureAuthorized) { // Fetch all rule type IDs and rule consumers, then run the provided ensureAuthorized check for each of them @@ -100,7 +111,11 @@ export async function setAlertsToUntracked({ size: 0, query: { bool: { - must, + must: getQuery({ + ruleIds, + alertUuids, + alertStatus: ALERT_STATUS_ACTIVE, + }), }, }, aggs: { @@ -141,7 +156,11 @@ export async function setAlertsToUntracked({ }, query: { bool: { - must, + must: getQuery({ + ruleIds, + alertUuids, + alertStatus: ALERT_STATUS_ACTIVE, + }), }, }, }, @@ -169,7 +188,11 @@ export async function setAlertsToUntracked({ size: total, query: { bool: { - must, + must: getQuery({ + ruleIds, + alertUuids, + alertStatus: ALERT_STATUS_UNTRACKED, + }), }, }, }, diff --git a/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/create_rule_data_schema.ts b/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/create_rule_data_schema.ts index 44a89e05992fe..9d1823381d17d 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/create_rule_data_schema.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/create_rule_data_schema.ts @@ -7,11 +7,7 @@ import { schema } from '@kbn/config-schema'; import { validateDuration } from '../../../validation'; -import { - notifyWhenSchema, - actionAlertsFilterSchema, - notificationDelaySchema, -} from '../../../schemas'; +import { notifyWhenSchema, actionAlertsFilterSchema, alertDelaySchema } from '../../../schemas'; export const createRuleDataSchema = schema.object({ name: schema.string(), @@ -44,5 +40,5 @@ export const createRuleDataSchema = schema.object({ { defaultValue: [] } ), notifyWhen: schema.maybe(schema.nullable(notifyWhenSchema)), - notificationDelay: schema.maybe(notificationDelaySchema), + alertDelay: schema.maybe(alertDelaySchema), }); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/create/types/create_rule_data.ts b/x-pack/plugins/alerting/server/application/rule/methods/create/types/create_rule_data.ts index f99beda90e80a..abee30ec9a524 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/create/types/create_rule_data.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/create/types/create_rule_data.ts @@ -22,5 +22,5 @@ export interface CreateRuleData { schedule: CreateRuleDataType['schedule']; actions: CreateRuleDataType['actions']; notifyWhen?: CreateRuleDataType['notifyWhen']; - notificationDelay?: CreateRuleDataType['notificationDelay']; + alertDelay?: CreateRuleDataType['alertDelay']; } diff --git a/x-pack/plugins/alerting/server/application/rule/schemas/index.ts b/x-pack/plugins/alerting/server/application/rule/schemas/index.ts index d039d190f1e96..06645e90d7baf 100644 --- a/x-pack/plugins/alerting/server/application/rule/schemas/index.ts +++ b/x-pack/plugins/alerting/server/application/rule/schemas/index.ts @@ -13,7 +13,7 @@ export { monitoringSchema, ruleSchema, ruleDomainSchema, - notificationDelaySchema, + alertDelaySchema, } from './rule_schemas'; export { diff --git a/x-pack/plugins/alerting/server/application/rule/schemas/rule_schemas.ts b/x-pack/plugins/alerting/server/application/rule/schemas/rule_schemas.ts index b75a6e4f76aad..6041e475daf52 100644 --- a/x-pack/plugins/alerting/server/application/rule/schemas/rule_schemas.ts +++ b/x-pack/plugins/alerting/server/application/rule/schemas/rule_schemas.ts @@ -132,7 +132,7 @@ export const snoozeScheduleSchema = schema.object({ skipRecurrences: schema.maybe(schema.arrayOf(schema.string())), }); -export const notificationDelaySchema = schema.object({ +export const alertDelaySchema = schema.object({ active: schema.number(), }); @@ -172,7 +172,7 @@ export const ruleDomainSchema = schema.object({ revision: schema.number(), running: schema.maybe(schema.nullable(schema.boolean())), viewInAppRelativeUrl: schema.maybe(schema.nullable(schema.string())), - notificationDelay: schema.maybe(notificationDelaySchema), + alertDelay: schema.maybe(alertDelaySchema), }); /** @@ -210,5 +210,5 @@ export const ruleSchema = schema.object({ revision: schema.number(), running: schema.maybe(schema.nullable(schema.boolean())), viewInAppRelativeUrl: schema.maybe(schema.nullable(schema.string())), - notificationDelay: schema.maybe(notificationDelaySchema), + alertDelay: schema.maybe(alertDelaySchema), }); diff --git a/x-pack/plugins/alerting/server/application/rule/transforms/transform_rule_attributes_to_rule_domain.ts b/x-pack/plugins/alerting/server/application/rule/transforms/transform_rule_attributes_to_rule_domain.ts index ea331cec4fcc3..9cc8131b1fa2d 100644 --- a/x-pack/plugins/alerting/server/application/rule/transforms/transform_rule_attributes_to_rule_domain.ts +++ b/x-pack/plugins/alerting/server/application/rule/transforms/transform_rule_attributes_to_rule_domain.ts @@ -216,7 +216,7 @@ export const transformRuleAttributesToRuleDomain = ( revision: ruleDomain.revision, running: ruleDomain.running, viewInAppRelativeUrl: ruleDomain.viewInAppRelativeUrl, - notificationDelay: ruleDomain.notificationDelay, + alertDelay: ruleDomain.alertDelay, }; if (isPublic) { diff --git a/x-pack/plugins/alerting/server/application/rule/transforms/transform_rule_domain_to_rule_attributes.ts b/x-pack/plugins/alerting/server/application/rule/transforms/transform_rule_domain_to_rule_attributes.ts index 2eb21c42587d3..e527bd6f31df0 100644 --- a/x-pack/plugins/alerting/server/application/rule/transforms/transform_rule_domain_to_rule_attributes.ts +++ b/x-pack/plugins/alerting/server/application/rule/transforms/transform_rule_domain_to_rule_attributes.ts @@ -68,6 +68,6 @@ export const transformRuleDomainToRuleAttributes = ( ...(rule.nextRun !== undefined ? { nextRun: rule.nextRun?.toISOString() || null } : {}), revision: rule.revision, ...(rule.running !== undefined ? { running: rule.running } : {}), - ...(rule.notificationDelay !== undefined ? { notificationDelay: rule.notificationDelay } : {}), + ...(rule.alertDelay !== undefined ? { alertDelay: rule.alertDelay } : {}), }; }; diff --git a/x-pack/plugins/alerting/server/application/rule/types/rule.ts b/x-pack/plugins/alerting/server/application/rule/types/rule.ts index 1a7e7e1d37118..f59056b382440 100644 --- a/x-pack/plugins/alerting/server/application/rule/types/rule.ts +++ b/x-pack/plugins/alerting/server/application/rule/types/rule.ts @@ -85,7 +85,7 @@ export interface Rule { revision: RuleSchemaType['revision']; running?: RuleSchemaType['running']; viewInAppRelativeUrl?: RuleSchemaType['viewInAppRelativeUrl']; - notificationDelay?: RuleSchemaType['notificationDelay']; + alertDelay?: RuleSchemaType['alertDelay']; } export interface RuleDomain { @@ -121,5 +121,5 @@ export interface RuleDomain { revision: RuleDomainSchemaType['revision']; running?: RuleDomainSchemaType['running']; viewInAppRelativeUrl?: RuleDomainSchemaType['viewInAppRelativeUrl']; - notificationDelay?: RuleSchemaType['notificationDelay']; + alertDelay?: RuleSchemaType['alertDelay']; } diff --git a/x-pack/plugins/alerting/server/data/rule/types/rule_attributes.ts b/x-pack/plugins/alerting/server/data/rule/types/rule_attributes.ts index 316578149c5ca..e047be1b9cddf 100644 --- a/x-pack/plugins/alerting/server/data/rule/types/rule_attributes.ts +++ b/x-pack/plugins/alerting/server/data/rule/types/rule_attributes.ts @@ -142,7 +142,7 @@ interface RuleMetaAttributes { versionApiKeyLastmodified?: string; } -interface NotificationDelayAttributes { +interface AlertDelayAttributes { active: number; } @@ -178,5 +178,5 @@ export interface RuleAttributes { nextRun?: string | null; revision: number; running?: boolean | null; - notificationDelay?: NotificationDelayAttributes; + alertDelay?: AlertDelayAttributes; } diff --git a/x-pack/plugins/alerting/server/integration_tests/__snapshots__/serverless_upgrade_and_rollback_checks.test.ts.snap b/x-pack/plugins/alerting/server/integration_tests/__snapshots__/serverless_upgrade_and_rollback_checks.test.ts.snap index f799fada643f3..70ffc475d01d6 100644 --- a/x-pack/plugins/alerting/server/integration_tests/__snapshots__/serverless_upgrade_and_rollback_checks.test.ts.snap +++ b/x-pack/plugins/alerting/server/integration_tests/__snapshots__/serverless_upgrade_and_rollback_checks.test.ts.snap @@ -7219,20 +7219,60 @@ Object { "type": "string", }, "params": Object { - "additionalProperties": false, - "properties": Object { - "command": Object { - "const": "isolate", - "type": "string", + "anyOf": Array [ + Object { + "additionalProperties": false, + "properties": Object { + "command": Object { + "const": "isolate", + "type": "string", + }, + "comment": Object { + "type": "string", + }, + }, + "required": Array [ + "command", + ], + "type": "object", }, - "comment": Object { - "type": "string", + Object { + "additionalProperties": false, + "properties": Object { + "command": Object { + "enum": Array [ + "kill-process", + "suspend-process", + ], + "type": "string", + }, + "comment": Object { + "type": "string", + }, + "config": Object { + "additionalProperties": false, + "properties": Object { + "field": Object { + "type": "string", + }, + "overwrite": Object { + "default": true, + "type": "boolean", + }, + }, + "required": Array [ + "field", + ], + "type": "object", + }, + }, + "required": Array [ + "command", + "config", + ], + "type": "object", }, - }, - "required": Array [ - "command", ], - "type": "object", }, }, "required": Array [ @@ -7857,20 +7897,60 @@ Object { "type": "string", }, "params": Object { - "additionalProperties": false, - "properties": Object { - "command": Object { - "const": "isolate", - "type": "string", + "anyOf": Array [ + Object { + "additionalProperties": false, + "properties": Object { + "command": Object { + "const": "isolate", + "type": "string", + }, + "comment": Object { + "type": "string", + }, + }, + "required": Array [ + "command", + ], + "type": "object", }, - "comment": Object { - "type": "string", + Object { + "additionalProperties": false, + "properties": Object { + "command": Object { + "enum": Array [ + "kill-process", + "suspend-process", + ], + "type": "string", + }, + "comment": Object { + "type": "string", + }, + "config": Object { + "additionalProperties": false, + "properties": Object { + "field": Object { + "type": "string", + }, + "overwrite": Object { + "default": true, + "type": "boolean", + }, + }, + "required": Array [ + "field", + ], + "type": "object", + }, + }, + "required": Array [ + "command", + "config", + ], + "type": "object", }, - }, - "required": Array [ - "command", ], - "type": "object", }, }, "required": Array [ diff --git a/x-pack/plugins/alerting/server/lib/get_alerts_for_notification.test.ts b/x-pack/plugins/alerting/server/lib/get_alerts_for_notification.test.ts index c1465d5b7a238..4656f4377f130 100644 --- a/x-pack/plugins/alerting/server/lib/get_alerts_for_notification.test.ts +++ b/x-pack/plugins/alerting/server/lib/get_alerts_for_notification.test.ts @@ -21,6 +21,7 @@ describe('getAlertsForNotification', () => { DEFAULT_FLAPPING_SETTINGS, true, 'default', + 0, { '1': alert1, }, @@ -89,6 +90,7 @@ describe('getAlertsForNotification', () => { DEFAULT_FLAPPING_SETTINGS, true, 'default', + 0, {}, {}, { @@ -222,6 +224,7 @@ describe('getAlertsForNotification', () => { DISABLE_FLAPPING_SETTINGS, true, 'default', + 0, {}, {}, { @@ -353,6 +356,7 @@ describe('getAlertsForNotification', () => { DEFAULT_FLAPPING_SETTINGS, false, 'default', + 0, {}, {}, { @@ -455,10 +459,11 @@ describe('getAlertsForNotification', () => { }); const alert2 = new Alert('2', { meta: { uuid: 'uuid-2' } }); - const { newAlerts, activeAlerts } = getAlertsForNotification( + const { newAlerts, activeAlerts, currentActiveAlerts } = getAlertsForNotification( DEFAULT_FLAPPING_SETTINGS, true, 'default', + 0, { '1': alert1, }, @@ -507,6 +512,30 @@ describe('getAlertsForNotification', () => { }, } `); + expect(currentActiveAlerts).toMatchInlineSnapshot(` + Object { + "1": Object { + "meta": Object { + "activeCount": 2, + "flappingHistory": Array [], + "maintenanceWindowIds": Array [], + "pendingRecoveredCount": 0, + "uuid": "uuid-1", + }, + "state": Object {}, + }, + "2": Object { + "meta": Object { + "activeCount": 1, + "flappingHistory": Array [], + "maintenanceWindowIds": Array [], + "pendingRecoveredCount": 0, + "uuid": "uuid-2", + }, + "state": Object {}, + }, + } + `); }); test('should reset activeCount for all recovered alerts', () => { @@ -517,6 +546,7 @@ describe('getAlertsForNotification', () => { DEFAULT_FLAPPING_SETTINGS, true, 'default', + 0, {}, {}, { @@ -574,4 +604,78 @@ describe('getAlertsForNotification', () => { } `); }); + + test('should remove the alert from newAlerts and should not return the alert in currentActiveAlerts if the activeCount is less than the rule alertDelay', () => { + const alert1 = new Alert('1', { + meta: { activeCount: 1, uuid: 'uuid-1' }, + }); + const alert2 = new Alert('2', { meta: { uuid: 'uuid-2' } }); + + const { newAlerts, activeAlerts, currentActiveAlerts } = getAlertsForNotification( + DEFAULT_FLAPPING_SETTINGS, + true, + 'default', + 5, + { + '1': alert1, + }, + { + '1': alert1, + '2': alert2, + }, + {}, + {} + ); + expect(newAlerts).toMatchInlineSnapshot(`Object {}`); + expect(activeAlerts).toMatchInlineSnapshot(` + Object { + "1": Object { + "meta": Object { + "activeCount": 2, + "flappingHistory": Array [], + "maintenanceWindowIds": Array [], + "pendingRecoveredCount": 0, + "uuid": "uuid-1", + }, + "state": Object {}, + }, + "2": Object { + "meta": Object { + "activeCount": 1, + "flappingHistory": Array [], + "maintenanceWindowIds": Array [], + "pendingRecoveredCount": 0, + "uuid": "uuid-2", + }, + "state": Object {}, + }, + } + `); + expect(currentActiveAlerts).toMatchInlineSnapshot(`Object {}`); + }); + + test('should update active alert to look like a new alert if the activeCount is equal to the rule alertDelay', () => { + const alert2 = new Alert('2', { meta: { uuid: 'uuid-2' } }); + + const { newAlerts, activeAlerts, currentActiveAlerts } = getAlertsForNotification( + DEFAULT_FLAPPING_SETTINGS, + true, + 'default', + 1, + {}, + { + '2': alert2, + }, + {}, + {} + ); + expect(newAlerts['2'].getState().duration).toBe('0'); + expect(newAlerts['2'].getState().start).toBeTruthy(); + + expect(activeAlerts['2'].getState().duration).toBe('0'); + expect(activeAlerts['2'].getState().start).toBeTruthy(); + + expect(currentActiveAlerts['2'].getState().duration).toBe('0'); + expect(currentActiveAlerts['2'].getState().start).toBeTruthy(); + }); }); diff --git a/x-pack/plugins/alerting/server/lib/get_alerts_for_notification.ts b/x-pack/plugins/alerting/server/lib/get_alerts_for_notification.ts index 63e95402549a4..593d92b35383b 100644 --- a/x-pack/plugins/alerting/server/lib/get_alerts_for_notification.ts +++ b/x-pack/plugins/alerting/server/lib/get_alerts_for_notification.ts @@ -19,10 +19,12 @@ export function getAlertsForNotification< flappingSettings: RulesSettingsFlappingProperties, notifyOnActionGroupChange: boolean, actionGroupId: string, + alertDelay: number, newAlerts: Record> = {}, activeAlerts: Record> = {}, recoveredAlerts: Record> = {}, - currentRecoveredAlerts: Record> = {} + currentRecoveredAlerts: Record> = {}, + startedAt?: string | null ) { const currentActiveAlerts: Record> = {}; @@ -30,7 +32,22 @@ export function getAlertsForNotification< const alert = activeAlerts[id]; alert.incrementActiveCount(); alert.resetPendingRecoveredCount(); - currentActiveAlerts[id] = alert; + // do not trigger an action notification if the number of consecutive + // active alerts is less than the rule alertDelay threshold + if (alert.getActiveCount() < alertDelay) { + // remove from new alerts + delete newAlerts[id]; + } else { + currentActiveAlerts[id] = alert; + // if the active count is equal to the alertDelay it is considered a new alert + if (alert.getActiveCount() === alertDelay) { + const currentTime = startedAt ?? new Date().toISOString(); + const state = alert.getState(); + // keep the state and update the start time and duration + alert.replaceState({ ...state, start: currentTime, duration: '0' }); + newAlerts[id] = alert; + } + } } for (const id of keys(currentRecoveredAlerts)) { diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/create/transforms/transform_create_body/v1.ts b/x-pack/plugins/alerting/server/routes/rule/apis/create/transforms/transform_create_body/v1.ts index fcf92b386aaa2..5dea295c40ed7 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/create/transforms/transform_create_body/v1.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/create/transforms/transform_create_body/v1.ts @@ -58,6 +58,6 @@ export const transformCreateBody = ( schedule: createBody.schedule, actions: transformCreateBodyActions(createBody.actions), ...(createBody.notify_when ? { notifyWhen: createBody.notify_when } : {}), - ...(createBody.notification_delay ? { notificationDelay: createBody.notification_delay } : {}), + ...(createBody.alert_delay ? { alertDelay: createBody.alert_delay } : {}), }; }; diff --git a/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/v1.ts b/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/v1.ts index 4f55401160ce9..f4fdedfc6f436 100644 --- a/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/v1.ts +++ b/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/v1.ts @@ -119,5 +119,5 @@ export const transformRuleToRuleResponse = ( ...(rule.viewInAppRelativeUrl !== undefined ? { view_in_app_relative_url: rule.viewInAppRelativeUrl } : {}), - ...(rule.notificationDelay !== undefined ? { notification_delay: rule.notificationDelay } : {}), + ...(rule.alertDelay !== undefined ? { alert_delay: rule.alertDelay } : {}), }); diff --git a/x-pack/plugins/alerting/server/saved_objects/schemas/raw_rule/v1.ts b/x-pack/plugins/alerting/server/saved_objects/schemas/raw_rule/v1.ts index 495c2493f2e43..e0641e9b275ea 100644 --- a/x-pack/plugins/alerting/server/saved_objects/schemas/raw_rule/v1.ts +++ b/x-pack/plugins/alerting/server/saved_objects/schemas/raw_rule/v1.ts @@ -213,7 +213,7 @@ const rawRuleActionSchema = schema.object({ useAlertDataForTemplate: schema.maybe(schema.boolean()), }); -export const notificationDelaySchema = schema.object({ +export const alertDelaySchema = schema.object({ active: schema.number(), }); @@ -274,5 +274,5 @@ export const rawRuleSchema = schema.object({ ), params: schema.recordOf(schema.string(), schema.maybe(schema.any())), typeVersion: schema.maybe(schema.number()), - notificationDelay: schema.maybe(notificationDelaySchema), + alertDelay: schema.maybe(alertDelaySchema), }); diff --git a/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts index 8f5147ea4de30..a742afb152b19 100644 --- a/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts @@ -2052,165 +2052,6 @@ describe('Execution Handler', () => { `); }); - test('does not schedule actions for alerts with activeCount less than the notificationDelay.active threshold', async () => { - const executionHandler = new ExecutionHandler( - generateExecutionParams({ - ...defaultExecutionParams, - rule: { - ...defaultExecutionParams.rule, - notificationDelay: { - active: 3, - }, - }, - }) - ); - - await executionHandler.run({ - ...generateAlert({ id: 1 }), - ...generateAlert({ id: 2, activeCount: 2 }), - }); - - expect(actionsClient.bulkEnqueueExecution).not.toHaveBeenCalled(); - expect(defaultExecutionParams.logger.debug).toHaveBeenCalledTimes(2); - - expect(defaultExecutionParams.logger.debug).toHaveBeenCalledWith( - 'no scheduling of action "1" for rule "1": the alert activeCount: 0 is less than the rule notificationDelay.active: 3 threshold.' - ); - expect(defaultExecutionParams.logger.debug).toHaveBeenCalledWith( - 'no scheduling of action "1" for rule "1": the alert activeCount: 2 is less than the rule notificationDelay.active: 3 threshold.' - ); - }); - - test('schedules actions for alerts with activeCount greater than or equal the notificationDelay.active threshold', async () => { - const executionHandler = new ExecutionHandler( - generateExecutionParams({ - ...defaultExecutionParams, - rule: { - ...defaultExecutionParams.rule, - notificationDelay: { - active: 3, - }, - }, - }) - ); - - await executionHandler.run({ - ...generateAlert({ id: 1, activeCount: 3 }), - ...generateAlert({ id: 2, activeCount: 4 }), - }); - - expect(actionsClient.bulkEnqueueExecution).toHaveBeenCalledTimes(1); - expect(actionsClient.bulkEnqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "actionTypeId": "test", - "apiKey": "MTIzOmFiYw==", - "consumer": "rule-consumer", - "executionId": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - "id": "1", - "params": Object { - "alertVal": "My 1 name-of-alert test1 tag-A,tag-B 1 goes here", - "contextVal": "My goes here", - "foo": true, - "stateVal": "My goes here", - }, - "relatedSavedObjects": Array [ - Object { - "id": "1", - "namespace": "test1", - "type": "alert", - "typeId": "test", - }, - ], - "source": Object { - "source": Object { - "id": "1", - "type": "alert", - }, - "type": "SAVED_OBJECT", - }, - "spaceId": "test1", - }, - Object { - "actionTypeId": "test", - "apiKey": "MTIzOmFiYw==", - "consumer": "rule-consumer", - "executionId": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - "id": "1", - "params": Object { - "alertVal": "My 1 name-of-alert test1 tag-A,tag-B 2 goes here", - "contextVal": "My goes here", - "foo": true, - "stateVal": "My goes here", - }, - "relatedSavedObjects": Array [ - Object { - "id": "1", - "namespace": "test1", - "type": "alert", - "typeId": "test", - }, - ], - "source": Object { - "source": Object { - "id": "1", - "type": "alert", - }, - "type": "SAVED_OBJECT", - }, - "spaceId": "test1", - }, - ], - ] - `); - }); - - test('schedules actions if notificationDelay.active threshold is not defined', async () => { - const executionHandler = new ExecutionHandler(generateExecutionParams()); - - await executionHandler.run({ - ...generateAlert({ id: 1, activeCount: 1 }), - }); - - expect(actionsClient.bulkEnqueueExecution).toHaveBeenCalledTimes(1); - expect(actionsClient.bulkEnqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "actionTypeId": "test", - "apiKey": "MTIzOmFiYw==", - "consumer": "rule-consumer", - "executionId": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - "id": "1", - "params": Object { - "alertVal": "My 1 name-of-alert test1 tag-A,tag-B 1 goes here", - "contextVal": "My goes here", - "foo": true, - "stateVal": "My goes here", - }, - "relatedSavedObjects": Array [ - Object { - "id": "1", - "namespace": "test1", - "type": "alert", - "typeId": "test", - }, - ], - "source": Object { - "source": Object { - "id": "1", - "type": "alert", - }, - "type": "SAVED_OBJECT", - }, - "spaceId": "test1", - }, - ], - ] - `); - }); - describe('rule url', () => { const ruleWithUrl = { ...rule, diff --git a/x-pack/plugins/alerting/server/task_runner/execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/execution_handler.ts index e118b4d327ce1..ec690bb8ba0f5 100644 --- a/x-pack/plugins/alerting/server/task_runner/execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/execution_handler.ts @@ -628,22 +628,6 @@ export class ExecutionHandler< continue; } - if ( - this.rule.notificationDelay && - alert.getActiveCount() < this.rule.notificationDelay.active - ) { - this.logger.debug( - `no scheduling of action "${action.id}" for rule "${ - this.taskInstance.params.alertId - }": the alert activeCount: ${alert.getActiveCount()} is less than the rule notificationDelay.active: ${ - this.rule.notificationDelay.active - } threshold.` - ); - continue; - } else { - alert.resetActiveCount(); - } - const actionGroup = this.getActionGroup(alert); if (!this.ruleTypeActionGroups!.has(actionGroup)) { diff --git a/x-pack/plugins/alerting/server/task_runner/fixtures.ts b/x-pack/plugins/alerting/server/task_runner/fixtures.ts index 3d647966414f5..b2a984ea5768f 100644 --- a/x-pack/plugins/alerting/server/task_runner/fixtures.ts +++ b/x-pack/plugins/alerting/server/task_runner/fixtures.ts @@ -454,7 +454,7 @@ export const generateAlertInstance = ( flapping: false, maintenanceWindowIds: maintenanceWindowIds || [], pendingRecoveredCount: 0, - activeCount: 0, + activeCount: 1, }, state: { bar: false, diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index d14d44010252d..e4afa351d4f14 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -2954,7 +2954,7 @@ describe('Task Runner', () => { maintenanceWindowIds: [], flapping: false, pendingRecoveredCount: 0, - activeCount: 0, + activeCount: 1, }, state: { duration: '0', @@ -3125,7 +3125,7 @@ describe('Task Runner', () => { maintenanceWindowIds: [], flapping: false, pendingRecoveredCount: 0, - activeCount: 0, + activeCount: 1, }, state: { duration: '0', @@ -3143,7 +3143,7 @@ describe('Task Runner', () => { maintenanceWindowIds: [], flapping: false, pendingRecoveredCount: 0, - activeCount: 0, + activeCount: 1, }, state: { duration: '0', diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index 208e46b88a1f6..93f655965e92a 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -257,6 +257,7 @@ export class TaskRunner< revision: rule.revision, spaceId, tags: rule.tags, + alertDelay: rule.alertDelay?.active ?? 0, }; } @@ -311,6 +312,7 @@ export class TaskRunner< muteAll, revision, snoozeSchedule, + alertDelay, } = rule; const { params: { alertId: ruleId, spaceId }, @@ -525,6 +527,7 @@ export class TaskRunner< notifyWhen, muteAll, snoozeSchedule, + alertDelay, }, logger: this.logger, flappingSettings, @@ -582,6 +585,7 @@ export class TaskRunner< notifyWhen === RuleNotifyWhen.CHANGE || some(actions, (action) => action.frequency?.notifyWhen === RuleNotifyWhen.CHANGE), maintenanceWindowIds: maintenanceWindowsWithoutScopedQueryIds, + alertDelay: alertDelay?.active ?? 0, }); }); diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_alerts_client.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_alerts_client.test.ts index ebf81f5bf050e..bd47acbbdb8c1 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_alerts_client.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_alerts_client.test.ts @@ -302,6 +302,7 @@ describe('Task Runner', () => { ruleType: ruleTypeWithAlerts, namespace: 'default', rule: { + alertDelay: 0, consumer: 'bar', executionId: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', id: '1', @@ -800,6 +801,7 @@ describe('Task Runner', () => { expect(alertsClientToUse.processAlerts).toHaveBeenCalledWith({ notifyOnActionGroupChange: false, + alertDelay: 0, flappingSettings: { enabled: true, lookBackWindow: 20, diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index ff16cfb593c28..eeb13576ce39d 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -62,7 +62,7 @@ import { AlertsFilter, AlertsFilterTimeframe, RuleAlertData, - NotificationDelay, + AlertDelay, } from '../common'; import { PublicAlertFactory } from './alert/create_alert_factory'; import { RulesSettingsFlappingProperties } from '../common/rules_settings'; @@ -423,7 +423,6 @@ export type PublicRuleResultService = PublicLastRunSetters; export interface RawRuleLastRun extends SavedObjectAttributes, RuleLastRun {} export interface RawRuleMonitoring extends SavedObjectAttributes, RuleMonitoring {} -export interface RawNotificationDelay extends SavedObjectAttributes, NotificationDelay {} export interface RawRuleAlertsFilter extends AlertsFilter { query?: { @@ -500,7 +499,7 @@ export interface RawRule extends SavedObjectAttributes { nextRun?: string | null; revision: number; running?: boolean | null; - notificationDelay?: RawNotificationDelay; + alertDelay?: AlertDelay; } export type { DataStreamAdapter } from './alerts_service/lib/data_stream_adapter'; diff --git a/x-pack/plugins/apm/public/components/app/dependency_detail_operations/dependency_detail_operations_list/index.tsx b/x-pack/plugins/apm/public/components/app/dependency_detail_operations/dependency_detail_operations_list/index.tsx index 35516c1da58fd..7b7a4089172bf 100644 --- a/x-pack/plugins/apm/public/components/app/dependency_detail_operations/dependency_detail_operations_list/index.tsx +++ b/x-pack/plugins/apm/public/components/app/dependency_detail_operations/dependency_detail_operations_list/index.tsx @@ -63,7 +63,8 @@ export function DependencyDetailOperationsList() { const { core } = useApmPluginContext(); - const breakpoints = useBreakpoints(); + const { isLarge } = useBreakpoints(); + const shouldShowSparkPlots = !isLarge; const { start, end } = useTimeRange({ rangeFrom, @@ -147,7 +148,7 @@ export function DependencyDetailOperationsList() { render: (_, { spanName }) => , }, ...getSpanMetricColumns({ - breakpoints, + shouldShowSparkPlots, comparisonFetchStatus: comparisonStatsFetch.status, }), ]; diff --git a/x-pack/plugins/apm/public/components/app/mobile/service_overview/index.tsx b/x-pack/plugins/apm/public/components/app/mobile/service_overview/index.tsx index 55104eefaa071..dd0e2cd9968cc 100644 --- a/x-pack/plugins/apm/public/components/app/mobile/service_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/mobile/service_overview/index.tsx @@ -193,7 +193,6 @@ export function MobileServiceOverview() { kuery={kueryWithMobileFilters} environment={environment} fixedHeight={true} - isSingleColumn={isSingleColumn} start={start} end={end} showPerPageOptions={false} diff --git a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx index 4b2ab0ad6c3a8..743843c825210 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx @@ -135,11 +135,11 @@ export function ServiceOverview() { kuery={kuery} environment={environment} fixedHeight={true} - isSingleColumn={isSingleColumn} start={start} end={end} showPerPageOptions={false} numberOfTransactionsPerPage={5} + showSparkPlots={!isSingleColumn} /> @@ -209,6 +209,7 @@ export function ServiceOverview() { )} } + showSparkPlots={!isSingleColumn} /> diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx index f95905dd3dc57..e8c9dffa375b0 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx @@ -24,12 +24,14 @@ interface ServiceOverviewDependenciesTableProps { fixedHeight?: boolean; link?: ReactNode; showPerPageOptions?: boolean; + showSparkPlots?: boolean; } export function ServiceOverviewDependenciesTable({ fixedHeight, link, showPerPageOptions = true, + showSparkPlots, }: ServiceOverviewDependenciesTableProps) { const { query: { @@ -171,6 +173,7 @@ export function ServiceOverviewDependenciesTable({ link={link} showPerPageOptions={showPerPageOptions} initialPageSize={5} + showSparkPlots={showSparkPlots} /> ); } diff --git a/x-pack/plugins/apm/public/components/shared/dependencies_table/get_span_metric_columns.tsx b/x-pack/plugins/apm/public/components/shared/dependencies_table/get_span_metric_columns.tsx index d477e7269cace..07d1b75ffb238 100644 --- a/x-pack/plugins/apm/public/components/shared/dependencies_table/get_span_metric_columns.tsx +++ b/x-pack/plugins/apm/public/components/shared/dependencies_table/get_span_metric_columns.tsx @@ -13,7 +13,6 @@ import { RIGHT_ALIGNMENT, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { Breakpoints } from '../../../hooks/use_breakpoints'; import { ChartType, getTimeSeriesColor, @@ -53,14 +52,12 @@ export interface SpanMetricGroup { } export function getSpanMetricColumns({ - breakpoints, comparisonFetchStatus, + shouldShowSparkPlots, }: { - breakpoints: Breakpoints; comparisonFetchStatus: FETCH_STATUS; + shouldShowSparkPlots: boolean; }): Array> { - const { isLarge } = breakpoints; - const shouldShowSparkPlots = !isLarge; const isLoading = isPending(comparisonFetchStatus); return [ diff --git a/x-pack/plugins/apm/public/components/shared/dependencies_table/index.tsx b/x-pack/plugins/apm/public/components/shared/dependencies_table/index.tsx index c4d1a0bdaffe1..dec4f35327e57 100644 --- a/x-pack/plugins/apm/public/components/shared/dependencies_table/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/dependencies_table/index.tsx @@ -38,6 +38,7 @@ interface Props { status: FETCH_STATUS; compact?: boolean; showPerPageOptions?: boolean; + showSparkPlots?: boolean; } type FormattedSpanMetricGroup = SpanMetricGroup & { @@ -56,10 +57,11 @@ export function DependenciesTable(props: Props) { compact = true, showPerPageOptions = true, initialPageSize, + showSparkPlots, } = props; - // SparkPlots should be hidden if we're in two-column view and size XL (1200px) - const breakpoints = useBreakpoints(); + const { isLarge } = useBreakpoints(); + const shouldShowSparkPlots = showSparkPlots ?? !isLarge; const items: FormattedSpanMetricGroup[] = dependencies.map((dependency) => ({ name: dependency.name, @@ -95,7 +97,7 @@ export function DependenciesTable(props: Props) { width: '30%', }, ...getSpanMetricColumns({ - breakpoints, + shouldShowSparkPlots, comparisonFetchStatus: status, }), ]; diff --git a/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx b/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx index e4553dfee073a..0d9621efd18c8 100644 --- a/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx @@ -48,7 +48,6 @@ const INITIAL_STATE: ApiResponse & { requestId: string } = { interface Props { hideTitle?: boolean; hideViewTransactionsLink?: boolean; - isSingleColumn?: boolean; numberOfTransactionsPerPage?: number; showPerPageOptions?: boolean; showMaxTransactionGroupsExceededWarning?: boolean; @@ -58,13 +57,13 @@ interface Props { start: string; end: string; saveTableOptionsToUrl?: boolean; + showSparkPlots?: boolean; } export function TransactionsTable({ fixedHeight = false, hideViewTransactionsLink = false, hideTitle = false, - isSingleColumn = true, numberOfTransactionsPerPage = 10, showPerPageOptions = true, showMaxTransactionGroupsExceededWarning = false, @@ -73,6 +72,7 @@ export function TransactionsTable({ start, end, saveTableOptionsToUrl = false, + showSparkPlots, }: Props) { const { link } = useApmRouter(); @@ -94,9 +94,8 @@ export function TransactionsTable({ latencyAggregationTypeFromQuery ); - // SparkPlots should be hidden if we're in two-column view and size XL (1200px) - const { isXl } = useBreakpoints(); - const shouldShowSparkPlots = isSingleColumn || !isXl; + const { isLarge } = useBreakpoints(); + const shouldShowSparkPlots = showSparkPlots ?? !isLarge; const { transactionType, serviceName } = useApmServiceContext(); const [searchQuery, setSearchQueryDebounced] = useStateDebounced(''); diff --git a/x-pack/plugins/cases/public/components/files/file_type.test.tsx b/x-pack/plugins/cases/public/components/files/file_type.test.tsx index 6a96870f14cf9..d9c58fd6cab2e 100644 --- a/x-pack/plugins/cases/public/components/files/file_type.test.tsx +++ b/x-pack/plugins/cases/public/components/files/file_type.test.tsx @@ -17,7 +17,8 @@ import { basicCase, basicFileMock } from '../../containers/mock'; import { getFileType } from './file_type'; import { FILE_ATTACHMENT_TYPE } from '../../../common/constants'; -describe('getFileType', () => { +// Failing: See https://github.com/elastic/kibana/issues/175841 +describe.skip('getFileType', () => { const fileType = getFileType(); it('invalid props return blank FileAttachmentViewObject', () => { diff --git a/x-pack/plugins/cloud_integrations/cloud_full_story/public/plugin.ts b/x-pack/plugins/cloud_integrations/cloud_full_story/public/plugin.ts index 7636d38b681e1..a9af52c9a6b31 100755 --- a/x-pack/plugins/cloud_integrations/cloud_full_story/public/plugin.ts +++ b/x-pack/plugins/cloud_integrations/cloud_full_story/public/plugin.ts @@ -77,7 +77,10 @@ export class CloudFullStoryPlugin implements Plugin { ...(pageVarsDebounceTime ? { pageVarsDebounceTimeMs: duration(pageVarsDebounceTime).asMilliseconds() } : {}), - // Load an Elastic-internally audited script. Ideally, it should be hosted on a CDN. + /** + * FIXME: this should use the {@link IStaticAssets['getPluginAssetHref']} + * function. Then we can avoid registering our own endpoint in this plugin. + */ scriptUrl: basePath.prepend( `/internal/cloud/${this.initializerContext.env.packageInfo.buildNum}/fullstory.js` ), diff --git a/x-pack/plugins/cloud_security_posture/common/types/rules/v4.ts b/x-pack/plugins/cloud_security_posture/common/types/rules/v4.ts index 78680bf111dc7..33134eed32e38 100644 --- a/x-pack/plugins/cloud_security_posture/common/types/rules/v4.ts +++ b/x-pack/plugins/cloud_security_posture/common/types/rules/v4.ts @@ -120,6 +120,7 @@ export interface BenchmarkRuleSelectParams { export interface PageUrlParams { benchmarkId: BenchmarksCisId; benchmarkVersion: string; + ruleId?: string; } export const rulesToUpdate = schema.arrayOf( diff --git a/x-pack/plugins/cloud_security_posture/public/common/navigation/constants.ts b/x-pack/plugins/cloud_security_posture/public/common/navigation/constants.ts index 674a28f34e97c..7390d2d846f11 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/navigation/constants.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/navigation/constants.ts @@ -63,7 +63,7 @@ export const cloudPosturePages: Record = { export const benchmarksNavigation: Record = { rules: { name: NAV_ITEMS_NAMES.RULES, - path: `${CLOUD_SECURITY_POSTURE_BASE_PATH}/benchmarks/:benchmarkId/:benchmarkVersion/rules`, + path: `${CLOUD_SECURITY_POSTURE_BASE_PATH}/benchmarks/:benchmarkId/:benchmarkVersion/rules/:ruleId?`, id: 'cloud_security_posture-benchmarks-rules', }, }; diff --git a/x-pack/plugins/cloud_security_posture/public/common/types.ts b/x-pack/plugins/cloud_security_posture/public/common/types.ts index d402ea2939062..f2881c1798883 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/types.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/types.ts @@ -17,6 +17,10 @@ export interface FindingsBaseURLQuery { * Filters that are part of the query but not persisted in the URL or in the Filter Manager */ nonPersistedFilters?: Filter[]; + /** + * Grouping component selection + */ + groupBy?: string[]; } export interface FindingsBaseESQueryConfig { diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/use_cloud_security_grouping.ts b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/use_cloud_security_grouping.ts index 05dc555bc3f7d..cd45c28f90d8a 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/use_cloud_security_grouping.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/use_cloud_security_grouping.ts @@ -69,8 +69,11 @@ export const useCloudSecurityGrouping = ({ groupingId: groupingLocalStorageKey, maxGroupingLevels, title: groupingTitle, - onGroupChange: () => { + onGroupChange: ({ groupByFields }) => { setActivePageIndex(0); + setUrlQuery({ + groupBy: groupByFields, + }); }, }); @@ -85,6 +88,16 @@ export const useCloudSecurityGrouping = ({ setActivePageIndex(0); }, [urlQuery.filters, urlQuery.query]); + /** + * Set the selected groups from the URL query on the initial render + */ + useEffect(() => { + if (urlQuery.groupBy) { + grouping.setSelectedGroups(urlQuery.groupBy); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + // This is recommended by the grouping component to cover an edge case // where the selectedGroup has multiple values const uniqueValue = useMemo(() => `${selectedGroup}-${uuid.v4()}`, [selectedGroup]); diff --git a/x-pack/plugins/cloud_security_posture/public/components/column_name_with_tooltip.tsx b/x-pack/plugins/cloud_security_posture/public/components/column_name_with_tooltip.tsx index e0b3da860a1cd..24f57dadea80a 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/column_name_with_tooltip.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/column_name_with_tooltip.tsx @@ -15,14 +15,14 @@ export const ColumnNameWithTooltip = ({ tooltipContent: EuiToolTipProps['content']; columnName: ReactNode; }) => ( - - - - {columnName} - - - - - - + + + {columnName} + + + + + + + ); diff --git a/x-pack/plugins/cloud_security_posture/public/components/vulnerability_badges.tsx b/x-pack/plugins/cloud_security_posture/public/components/vulnerability_badges.tsx index ff8924833a294..25a77b3328768 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/vulnerability_badges.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/vulnerability_badges.tsx @@ -24,8 +24,10 @@ interface SeverityStatusBadgeProps { export const CVSScoreBadge = ({ score, version }: CVSScoreBadgeProps) => { if (!score) return null; + const color = getCvsScoreColor(score); const versionDisplay = version ? `v${version.split('.')[0]}` : null; + return ( { + const { application } = useKibana().services; + + const ruleFlyoutLink = application.getUrlForApp('security', { + path: generatePath(benchmarksNavigation.rules.path, { + benchmarkVersion: findings.rule.benchmark.version.split('v')[1], // removing the v from the version + benchmarkId: findings.rule.benchmark.id, + ruleId: findings.rule.id, + }), + }); + switch (tab.id) { case 'overview': - return ; + return ; case 'rule': - return ; + return ; case 'table': return ; case 'json': diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/overview_tab.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/overview_tab.tsx index e6e5b1386652d..42808c02f5050 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/overview_tab.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/overview_tab.tsx @@ -13,6 +13,7 @@ import { EuiPanel, EuiSpacer, EuiText, + EuiToolTip, } from '@elastic/eui'; import React, { useMemo } from 'react'; import moment from 'moment'; @@ -36,12 +37,21 @@ import { FindingsDetectionRuleCounter } from './findings_detection_rule_counter' type Accordion = Pick & Pick; -const getDetailsList = (data: CspFinding, discoverIndexLink: string | undefined) => [ +const getDetailsList = (data: CspFinding, ruleFlyoutLink: string, discoverIndexLink?: string) => [ { title: i18n.translate('xpack.csp.findings.findingsFlyout.overviewTab.ruleNameTitle', { defaultMessage: 'Rule Name', }), - description: data.rule.name, + description: ( + + {data.rule.name} + + ), }, { title: i18n.translate('xpack.csp.findings.findingsFlyout.overviewTab.alertsTitle', { @@ -160,10 +170,14 @@ const getEvidenceList = ({ result }: CspFinding) => }, ].filter(truthy); -export const OverviewTab = ({ data }: { data: CspFinding }) => { - const { - services: { discover }, - } = useKibana(); +export const OverviewTab = ({ + data, + ruleFlyoutLink, +}: { + data: CspFinding; + ruleFlyoutLink: string; +}) => { + const { discover } = useKibana().services; const latestFindingsDataView = useLatestFindingsDataView(LATEST_FINDINGS_INDEX_PATTERN); const discoverIndexLink = useMemo( @@ -185,7 +199,7 @@ export const OverviewTab = ({ data }: { data: CspFinding }) => { defaultMessage: 'Details', }), id: 'detailsAccordion', - listItems: getDetailsList(data, discoverIndexLink), + listItems: getDetailsList(data, ruleFlyoutLink, discoverIndexLink), }, { initialIsOpen: true, @@ -206,7 +220,7 @@ export const OverviewTab = ({ data }: { data: CspFinding }) => { listItems: getEvidenceList(data), }, ].filter(truthy), - [data, discoverIndexLink, hasEvidence] + [data, discoverIndexLink, hasEvidence, ruleFlyoutLink] ); return ( diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/rule_tab.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/rule_tab.tsx index 816717943bddd..9b7b400a58196 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/rule_tab.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/rule_tab.tsx @@ -5,18 +5,35 @@ * 2.0. */ -import { EuiBadge, EuiDescriptionList } from '@elastic/eui'; +import { EuiBadge, EuiDescriptionList, EuiLink, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; import { CspFinding } from '../../../../common/schemas/csp_finding'; +import { RulesDetectionRuleCounter } from '../../rules/rules_detection_rule_counter'; import { CisKubernetesIcons, CspFlyoutMarkdown } from './findings_flyout'; -export const getRuleList = (rule: CspFinding['rule']) => [ +export const getRuleList = ( + rule: CspFinding['rule'], + ruleState = 'unmuted', + ruleFlyoutLink?: string +) => [ { title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.nameTitle', { defaultMessage: 'Name', }), - description: rule.name, + description: ruleFlyoutLink ? ( + + {rule.name} + + ) : ( + rule.name + ), }, { title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.descriptionTitle', { @@ -24,6 +41,20 @@ export const getRuleList = (rule: CspFinding['rule']) => [ }), description: {rule.description}, }, + { + title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.AlertsTitle', { + defaultMessage: 'Alerts', + }), + description: + ruleState === 'unmuted' ? ( + + ) : ( + + ), + }, { title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.tagsTitle', { defaultMessage: 'Tags', @@ -80,6 +111,6 @@ export const getRuleList = (rule: CspFinding['rule']) => [ : []), ]; -export const RuleTab = ({ data }: { data: CspFinding }) => ( - -); +export const RuleTab = ({ data, ruleFlyoutLink }: { data: CspFinding; ruleFlyoutLink: string }) => { + return ; +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/utils/create_detection_rule_from_benchmark.ts b/x-pack/plugins/cloud_security_posture/public/pages/configurations/utils/create_detection_rule_from_benchmark.ts new file mode 100644 index 0000000000000..f445761ed8447 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/utils/create_detection_rule_from_benchmark.ts @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { HttpSetup } from '@kbn/core/public'; +import { CspBenchmarkRule } from '../../../../common/types/latest'; +import { + FINDINGS_INDEX_PATTERN, + LATEST_FINDINGS_RETENTION_POLICY, +} from '../../../../common/constants'; +import { createDetectionRule } from '../../../common/api/create_detection_rule'; +import { generateBenchmarkRuleTags } from '../../../../common/utils/detection_rules'; + +const DEFAULT_RULE_RISK_SCORE = 0; +const DEFAULT_RULE_SEVERITY = 'low'; +const DEFAULT_RULE_ENABLED = true; +const DEFAULT_RULE_AUTHOR = 'Elastic'; +const DEFAULT_RULE_LICENSE = 'Elastic License v2'; +const DEFAULT_MAX_ALERTS_PER_RULE = 100; +const ALERT_SUPPRESSION_FIELD = 'resource.id'; +const ALERT_TIMESTAMP_FIELD = 'event.ingested'; +const DEFAULT_INVESTIGATION_FIELDS = { + field_names: ['resource.name', 'resource.id', 'resource.type', 'resource.sub_type'], +}; + +enum AlertSuppressionMissingFieldsStrategy { + // per each document a separate alert will be created + DoNotSuppress = 'doNotSuppress', + // only one alert will be created per suppress by bucket + Suppress = 'suppress', +} + +const convertReferencesLinksToArray = (input: string | undefined) => { + if (!input) { + return []; + } + // Match all URLs in the input string using a regular expression + const matches = input.match(/(https?:\/\/\S+)/g); + + if (!matches) { + return []; + } + + // Remove the numbers and new lines + return matches.map((link) => link.replace(/^\d+\. /, '').replace(/\n/g, '')); +}; + +const generateFindingsRuleQuery = (benchmarkRule: CspBenchmarkRule['metadata']) => { + const currentTimestamp = new Date().toISOString(); + + return `rule.benchmark.rule_number: "${benchmarkRule.benchmark.rule_number}" + AND rule.benchmark.id: "${benchmarkRule.benchmark.id}" + AND result.evaluation: "failed" + AND event.ingested >= "${currentTimestamp}"`; +}; + +/* + * Creates a detection rule from a Benchmark rule + */ +export const createDetectionRuleFromBenchmark = async ( + http: HttpSetup, + benchmarkRule: CspBenchmarkRule['metadata'] +) => { + return await createDetectionRule({ + http, + rule: { + type: 'query', + language: 'kuery', + license: DEFAULT_RULE_LICENSE, + author: [DEFAULT_RULE_AUTHOR], + filters: [], + false_positives: [], + risk_score: DEFAULT_RULE_RISK_SCORE, + risk_score_mapping: [], + severity: DEFAULT_RULE_SEVERITY, + severity_mapping: [], + threat: [], + interval: '1h', + from: `now-${LATEST_FINDINGS_RETENTION_POLICY}`, + to: 'now', + max_signals: DEFAULT_MAX_ALERTS_PER_RULE, + timestamp_override: ALERT_TIMESTAMP_FIELD, + timestamp_override_fallback_disabled: false, + actions: [], + enabled: DEFAULT_RULE_ENABLED, + alert_suppression: { + group_by: [ALERT_SUPPRESSION_FIELD], + missing_fields_strategy: AlertSuppressionMissingFieldsStrategy.Suppress, + }, + index: [FINDINGS_INDEX_PATTERN], + query: generateFindingsRuleQuery(benchmarkRule), + references: convertReferencesLinksToArray(benchmarkRule.references), + name: benchmarkRule.name, + description: benchmarkRule.rationale, + tags: generateBenchmarkRuleTags(benchmarkRule), + investigation_fields: DEFAULT_INVESTIGATION_FIELDS, + note: benchmarkRule.remediation, + }, + }); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.tsx index 62d1371b01f3f..d50a351a0f1b6 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.tsx @@ -4,10 +4,11 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useState, useMemo } from 'react'; +import React, { useState, useMemo, useEffect } from 'react'; import compareVersions from 'compare-versions'; import { EuiSpacer } from '@elastic/eui'; -import { useParams } from 'react-router-dom'; +import { useParams, useHistory, generatePath } from 'react-router-dom'; +import { benchmarksNavigation } from '../../common/navigation/constants'; import { buildRuleKey } from '../../../common/utils/rules_states'; import { extractErrorMessage } from '../../../common/utils/helpers'; import { RulesTable } from './rules_table'; @@ -41,7 +42,10 @@ interface RulesPageData { export type RulesState = RulesPageData & RulesQuery; -const getRulesPage = ( +const getPage = (data: CspBenchmarkRulesWithStates[], { page, perPage }: RulesQuery) => + data.slice(page * perPage, (page + 1) * perPage); + +const getRulesPageData = ( data: CspBenchmarkRulesWithStates[], status: string, error: unknown, @@ -59,17 +63,45 @@ const getRulesPage = ( }; }; -const getPage = (data: CspBenchmarkRulesWithStates[], { page, perPage }: RulesQuery) => - data.slice(page * perPage, (page + 1) * perPage); - const MAX_ITEMS_PER_PAGE = 10000; export const RulesContainer = () => { const params = useParams(); - const [selectedRuleId, setSelectedRuleId] = useState(null); + const history = useHistory(); const [enabledDisabledItemsFilter, setEnabledDisabledItemsFilter] = useState('no-filter'); const { pageSize, setPageSize } = usePageSize(LOCAL_STORAGE_PAGE_SIZE_RULES_KEY); + const navToRuleFlyout = (ruleId: string) => { + history.push( + generatePath(benchmarksNavigation.rules.path, { + benchmarkVersion: params.benchmarkVersion, + benchmarkId: params.benchmarkId, + ruleId, + }) + ); + }; + + const navToRulePage = () => { + history.push( + generatePath(benchmarksNavigation.rules.path, { + benchmarkVersion: params.benchmarkVersion, + benchmarkId: params.benchmarkId, + }) + ); + }; + + // We need to make this call without filters. this way the section list is always full + const allRules = useFindCspBenchmarkRule( + { + page: 1, + perPage: MAX_ITEMS_PER_PAGE, + sortField: 'metadata.benchmark.rule_number', + sortOrder: 'asc', + }, + params.benchmarkId, + params.benchmarkVersion + ); + const [rulesQuery, setRulesQuery] = useState({ section: undefined, ruleNumber: undefined, @@ -80,6 +112,30 @@ export const RulesContainer = () => { sortOrder: 'asc', }); + // This useEffect is in charge of auto paginating to the correct page of a rule from the url params + useEffect(() => { + const getPageByRuleId = () => { + if (params.ruleId && allRules.data?.items) { + const ruleIndex = allRules.data.items.findIndex( + (rule) => rule.metadata.id === params.ruleId + ); + + if (ruleIndex !== -1) { + // Calculate the page based on the rule index and page size + const rulePage = Math.floor(ruleIndex / pageSize); + return rulePage; + } + } + return 0; + }; + + setRulesQuery({ + ...rulesQuery, + page: getPageByRuleId(), + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [allRules.data?.items]); + const { data, status, error } = useFindCspBenchmarkRule( { section: rulesQuery.section, @@ -94,27 +150,9 @@ export const RulesContainer = () => { params.benchmarkVersion ); - // We need to make this call again without the filters. this way the section list is always full - const allRules = useFindCspBenchmarkRule( - { - page: 1, - perPage: MAX_ITEMS_PER_PAGE, - sortField: 'metadata.benchmark.rule_number', - sortOrder: 'asc', - }, - params.benchmarkId, - params.benchmarkVersion - ); - const rulesStates = useCspGetRulesStates(); const arrayRulesStates: RuleStateAttributes[] = Object.values(rulesStates.data || {}); - const filteredRulesStates: RuleStateAttributes[] = arrayRulesStates.filter( - (ruleState: RuleStateAttributes) => - ruleState.benchmark_id === params.benchmarkId && - ruleState.benchmark_version === 'v' + params.benchmarkVersion - ); - const rulesWithStates: CspBenchmarkRulesWithStates[] = useMemo(() => { if (!data) return []; @@ -162,7 +200,7 @@ export const RulesContainer = () => { const cleanedRuleNumberList = [...new Set(ruleNumberList)].sort(compareVersions); const rulesPageData = useMemo( - () => getRulesPage(filteredRulesWithStates, status, error, rulesQuery), + () => getRulesPageData(filteredRulesWithStates, status, error, rulesQuery), [filteredRulesWithStates, status, error, rulesQuery] ); @@ -175,13 +213,14 @@ export const RulesContainer = () => { const rulesFlyoutData: CspBenchmarkRulesWithStates = { ...{ state: - filteredRulesStates.find( - (filteredRuleState) => filteredRuleState.rule_id === selectedRuleId - )?.muted === true + arrayRulesStates.find((filteredRuleState) => filteredRuleState.rule_id === params.ruleId) + ?.muted === true ? 'muted' : 'unmuted', }, - ...{ metadata: rulesPageData.rules_map.get(selectedRuleId!)?.metadata! }, + ...{ + metadata: allRules.data?.items.find((rule) => rule.metadata.id === params.ruleId)?.metadata!, + }, }; return ( @@ -227,16 +266,16 @@ export const RulesContainer = () => { setPageSize(paginationQuery.perPage); setRulesQuery((currentQuery) => ({ ...currentQuery, ...paginationQuery })); }} - setSelectedRuleId={setSelectedRuleId} - selectedRuleId={selectedRuleId} + selectedRuleId={params.ruleId} + onRuleClick={navToRuleFlyout} refetchRulesStates={rulesStates.refetch} selectedRules={selectedRules} setSelectedRules={setSelectedRules} /> - {selectedRuleId && ( + {params.ruleId && rulesFlyoutData.metadata && ( setSelectedRuleId(null)} + onClose={navToRulePage} refetchRulesStates={rulesStates.refetch} /> )} diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_detection_rule_counter.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_detection_rule_counter.tsx new file mode 100644 index 0000000000000..04b6a4ab83597 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_detection_rule_counter.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 type { HttpSetup } from '@kbn/core/public'; +import React from 'react'; +import { CspBenchmarkRule } from '../../../common/types/latest'; +import { getFindingsDetectionRuleSearchTags } from '../../../common/utils/detection_rules'; +import { DetectionRuleCounter } from '../../components/detection_rule_counter'; +import { createDetectionRuleFromBenchmark } from '../configurations/utils/create_detection_rule_from_benchmark'; + +export const RulesDetectionRuleCounter = ({ + benchmarkRule, +}: { + benchmarkRule: CspBenchmarkRule['metadata']; +}) => { + const createBenchmarkRuleFn = async (http: HttpSetup) => + await createDetectionRuleFromBenchmark(http, benchmarkRule); + + return ( + + ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_flyout.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_flyout.tsx index dbfcb6df75a98..333f958de6fb1 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_flyout.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_flyout.tsx @@ -17,9 +17,12 @@ import { EuiFlexGroup, EuiSwitch, EuiFlyoutFooter, + EuiIcon, + EuiToolTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; import { CspBenchmarkRuleMetadata } from '../../../common/types/latest'; import { getRuleList } from '../configurations/findings_flyout/rule_tab'; import { getRemediationList } from '../configurations/findings_flyout/overview_tab'; @@ -73,6 +76,7 @@ export const RuleFlyout = ({ onClose, rule, refetchRulesStates }: RuleFlyoutProp await refetchRulesStates(); } }; + return ( @@ -144,9 +148,32 @@ const RuleOverviewTab = ({ const ruleState = (rule: CspBenchmarkRulesWithStates, switchRuleStates: () => Promise) => [ { - title: i18n.translate('xpack.csp.rules.rulesFlyout.ruleState', { - defaultMessage: 'Enabled', - }), + title: ( + + + + + + + + + + + ), description: ( <> & { setPagination(pagination: Pick): void; - setSelectedRuleId(id: string | null): void; - selectedRuleId: string | null; + onRuleClick: (ruleID: string) => void; + selectedRuleId?: string; refetchRulesStates: () => void; selectedRules: CspBenchmarkRulesWithStates[]; setSelectedRules: (rules: CspBenchmarkRulesWithStates[]) => void; @@ -42,7 +43,7 @@ type RulesTableProps = Pick< type GetColumnProps = Pick< RulesTableProps, - 'setSelectedRuleId' | 'refetchRulesStates' | 'selectedRules' | 'setSelectedRules' + 'onRuleClick' | 'refetchRulesStates' | 'selectedRules' | 'setSelectedRules' > & { postRequestChangeRulesStates: ( actionOnRule: 'mute' | 'unmute', @@ -59,7 +60,6 @@ type GetColumnProps = Pick< export const RulesTable = ({ setPagination, - setSelectedRuleId, perPage: pageSize, rules_page: items, page, @@ -70,6 +70,7 @@ export const RulesTable = ({ refetchRulesStates, selectedRules, setSelectedRules, + onRuleClick, onSortChange, }: RulesTableProps) => { const { euiTheme } = useEuiTheme(); @@ -133,7 +134,6 @@ export const RulesTable = ({ const columns = useMemo( () => getColumns({ - setSelectedRuleId, refetchRulesStates, postRequestChangeRulesStates, selectedRules, @@ -142,15 +142,16 @@ export const RulesTable = ({ setIsAllRulesSelectedThisPage, isAllRulesSelectedThisPage, isCurrentPageRulesASubset, + onRuleClick, }), [ - setSelectedRuleId, refetchRulesStates, postRequestChangeRulesStates, selectedRules, setSelectedRules, items, isAllRulesSelectedThisPage, + onRuleClick, ] ); @@ -173,7 +174,6 @@ export const RulesTable = ({ }; const getColumns = ({ - setSelectedRuleId, refetchRulesStates, postRequestChangeRulesStates, selectedRules, @@ -181,6 +181,7 @@ const getColumns = ({ items, isAllRulesSelectedThisPage, isCurrentPageRulesASubset, + onRuleClick, }: GetColumnProps): Array> => [ { field: 'action', @@ -255,7 +256,7 @@ const getColumns = ({ title={name} onClick={(e: React.MouseEvent) => { e.stopPropagation(); - setSelectedRuleId(rule.metadata.id); + onRuleClick(rule.metadata.id); }} data-test-subj={TEST_SUBJECTS.CSP_RULES_TABLE_ROW_ITEM_NAME} > @@ -272,9 +273,16 @@ const getColumns = ({ }, { field: 'metadata.name', - name: i18n.translate('xpack.csp.rules.rulesTable.mutedColumnLabel', { - defaultMessage: 'Enabled', - }), + name: ( + + ), align: 'right', width: '100px', truncateText: true, diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_string_field_stats.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_string_field_stats.ts index f3b70085de33f..3796d4424f41e 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_string_field_stats.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_string_field_stats.ts @@ -83,6 +83,7 @@ export const fetchStringFieldsStats = ( ), map((resp) => { if (!isIKibanaSearchResponse(resp)) return resp; + const aggregations = resp.rawResponse.aggregations; const aggsPath = ['sample']; @@ -97,7 +98,9 @@ export const fetchStringFieldsStats = ( const { topValuesSampleSize, topValues } = processTopValues( fieldAgg, - get(aggregations, ['sample', 'doc_count']) + get(aggregations, ['sample', 'probability']) < 1 + ? get(aggregations, ['sample', 'doc_count']) + : undefined ); const stats = { fieldName: field.fieldName, diff --git a/x-pack/plugins/ecs_data_quality_dashboard/kibana.jsonc b/x-pack/plugins/ecs_data_quality_dashboard/kibana.jsonc index 2650184783066..5adbe3eeee830 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/kibana.jsonc +++ b/x-pack/plugins/ecs_data_quality_dashboard/kibana.jsonc @@ -1,7 +1,7 @@ { "type": "plugin", "id": "@kbn/ecs-data-quality-dashboard-plugin", - "owner": "@elastic/security-threat-hunting-investigations", + "owner": "@elastic/security-threat-hunting-explore", "description": "APIs used to assess the quality of data in Elasticsearch indexes", "plugin": { "id": "ecsDataQualityDashboard", diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/lib/data_stream/results_field_map.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/lib/data_stream/results_field_map.ts index 59f8ade6cb834..c0b929ec6deb8 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/lib/data_stream/results_field_map.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/lib/data_stream/results_field_map.ts @@ -8,33 +8,23 @@ import type { FieldMap } from '@kbn/data-stream-adapter'; export const resultsFieldMap: FieldMap = { - 'meta.batchId': { type: 'keyword', required: true }, - 'meta.ecsVersion': { type: 'keyword', required: true }, - 'meta.errorCount': { type: 'long', required: true }, - 'meta.ilmPhase': { type: 'keyword', required: true }, - 'meta.indexId': { type: 'keyword', required: true }, - 'meta.indexName': { type: 'keyword', required: true }, - 'meta.isCheckAll': { type: 'boolean', required: true }, - 'meta.numberOfDocuments': { type: 'long', required: true }, - 'meta.numberOfFields': { type: 'long', required: true }, - 'meta.numberOfIncompatibleFields': { type: 'long', required: true }, - 'meta.numberOfEcsFields': { type: 'long', required: true }, - 'meta.numberOfCustomFields': { type: 'long', required: true }, - 'meta.numberOfIndices': { type: 'long', required: true }, - 'meta.numberOfIndicesChecked': { type: 'long', required: true }, - 'meta.numberOfSameFamily': { type: 'long', required: true }, - 'meta.sameFamilyFields': { type: 'keyword', required: true, array: true }, - 'meta.sizeInBytes': { type: 'long', required: true }, - 'meta.timeConsumedMs': { type: 'long', required: true }, - 'meta.unallowedMappingFields': { type: 'keyword', required: true, array: true }, - 'meta.unallowedValueFields': { type: 'keyword', required: true, array: true }, - 'rollup.docsCount': { type: 'long', required: true }, - 'rollup.error': { type: 'text', required: false }, - 'rollup.ilmExplainPhaseCounts': { type: 'object', required: false }, - 'rollup.indices': { type: 'long', required: true }, - 'rollup.pattern': { type: 'keyword', required: true }, - 'rollup.sizeInBytes': { type: 'long', required: true }, - 'rollup.ilmExplain': { type: 'object', required: true, array: true }, - 'rollup.stats': { type: 'object', required: true, array: true }, - 'rollup.results': { type: 'object', required: true, array: true }, + batchId: { type: 'keyword', required: true }, + indexName: { type: 'keyword', required: true }, + isCheckAll: { type: 'boolean', required: true }, + checkedAt: { type: 'date', required: true }, + docsCount: { type: 'long', required: true }, + totalFieldCount: { type: 'long', required: true }, + ecsFieldCount: { type: 'long', required: true }, + customFieldCount: { type: 'long', required: true }, + incompatibleFieldCount: { type: 'long', required: true }, + sameFamilyFieldCount: { type: 'long', required: true }, + sameFamilyFields: { type: 'keyword', required: true, array: true }, + unallowedMappingFields: { type: 'keyword', required: true, array: true }, + unallowedValueFields: { type: 'keyword', required: true, array: true }, + sizeInBytes: { type: 'long', required: true }, + ilmPhase: { type: 'keyword', required: true }, + markdownComments: { type: 'text', required: true, array: true }, + ecsVersion: { type: 'keyword', required: true }, + indexId: { type: 'keyword', required: true }, + error: { type: 'text', required: false }, }; diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/plugin.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/plugin.ts index 19c6f12479694..cb6fe9da7c276 100755 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/plugin.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/plugin.ts @@ -48,14 +48,13 @@ export class EcsDataQualityDashboardPlugin public setup(core: CoreSetup, plugins: PluginSetupDependencies) { this.logger.debug('ecsDataQualityDashboard: Setup'); - // TODO: Uncomment https://github.com/elastic/kibana/pull/173185#issuecomment-1908034302 - // this.resultsDataStream.install({ - // esClient: core - // .getStartServices() - // .then(([{ elasticsearch }]) => elasticsearch.client.asInternalUser), - // logger: this.logger, - // pluginStop$: this.pluginStop$, - // }); + this.resultsDataStream.install({ + esClient: core + .getStartServices() + .then(([{ elasticsearch }]) => elasticsearch.client.asInternalUser), + logger: this.logger, + pluginStop$: this.pluginStop$, + }); core.http.registerRouteHandlerContext< DataQualityDashboardRequestHandlerContext, 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 73282d11e3d71..31202adffed2c 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,6 +19,7 @@ export const getILMExplainRoute = (router: IRouter, logger: Logger) => { .get({ path: GET_ILM_EXPLAIN, access: 'internal', + options: { tags: ['access: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 e593320933f7c..f3c59ccf9f3e2 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,6 +19,7 @@ export const getIndexMappingsRoute = (router: IRouter, logger: Logger) => { .get({ path: GET_INDEX_MAPPINGS, access: 'internal', + options: { tags: ['access:securitySolution'] }, }) .addVersion( { 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 cbaf7940a4b51..69d49b8611101 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 @@ -20,6 +20,7 @@ export const getIndexStatsRoute = (router: IRouter, logger: Logger) => { .get({ path: GET_INDEX_STATS, access: 'internal', + options: { tags: ['access:securitySolution'] }, }) .addVersion( { diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.test.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.test.ts index 44f7a97abf0d0..05a714a27275a 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.test.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.test.ts @@ -12,19 +12,17 @@ import { requestContextMock } from '../../__mocks__/request_context'; import type { LatestAggResponseBucket } from './get_results'; import { getResultsRoute, getQuery } from './get_results'; import { loggerMock, type MockedLogger } from '@kbn/logging-mocks'; -import { resultBody, resultDocument } from './results.mock'; -import type { - SearchResponse, - SecurityHasPrivilegesResponse, -} from '@elastic/elasticsearch/lib/api/types'; +import { resultDocument } from './results.mock'; +import type { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; import type { ResultDocument } from '../../schemas/result'; +import type { CheckIndicesPrivilegesParam } from './privileges'; const searchResponse = { aggregations: { latest: { buckets: [ { - key: 'logs-*', + key: resultDocument.indexName, latest_doc: { hits: { hits: [{ _source: resultDocument }] } }, }, ], @@ -35,8 +33,15 @@ const searchResponse = { Record >; -// TODO: https://github.com/elastic/kibana/pull/173185#issuecomment-1908034302 -describe.skip('getResultsRoute route', () => { +const mockCheckIndicesPrivileges = jest.fn(({ indices }: CheckIndicesPrivilegesParam) => + Promise.resolve(Object.fromEntries(indices.map((index) => [index, true]))) +); +jest.mock('./privileges', () => ({ + checkIndicesPrivileges: (params: CheckIndicesPrivilegesParam) => + mockCheckIndicesPrivileges(params), +})); + +describe('getResultsRoute route', () => { describe('querying', () => { let server: ReturnType; let { context } = requestContextMock.createTools(); @@ -45,7 +50,7 @@ describe.skip('getResultsRoute route', () => { const req = requestMock.create({ method: 'get', path: RESULTS_ROUTE_PATH, - query: { patterns: 'logs-*,alerts-*' }, + query: { pattern: 'logs-*' }, }); beforeEach(() => { @@ -56,9 +61,9 @@ describe.skip('getResultsRoute route', () => { ({ context } = requestContextMock.createTools()); - context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges.mockResolvedValue({ - index: { 'logs-*': { all: true }, 'alerts-*': { all: true } }, - } as unknown as SecurityHasPrivilegesResponse); + context.core.elasticsearch.client.asInternalUser.indices.get.mockResolvedValue({ + [resultDocument.indexName]: {}, + }); getResultsRoute(server.router, logger); }); @@ -68,10 +73,13 @@ describe.skip('getResultsRoute route', () => { mockSearch.mockResolvedValueOnce(searchResponse); const response = await server.inject(req, requestContextMock.convertContext(context)); - expect(mockSearch).toHaveBeenCalled(); + expect(mockSearch).toHaveBeenCalledWith({ + index: expect.any(String), + ...getQuery([resultDocument.indexName]), + }); expect(response.status).toEqual(200); - expect(response.body).toEqual([{ '@timestamp': expect.any(Number), ...resultBody }]); + expect(response.body).toEqual([resultDocument]); }); it('handles results data stream error', async () => { @@ -99,7 +107,7 @@ describe.skip('getResultsRoute route', () => { }); }); - describe('request pattern authorization', () => { + describe('request indices authorization', () => { let server: ReturnType; let { context } = requestContextMock.createTools(); let logger: MockedLogger; @@ -107,7 +115,7 @@ describe.skip('getResultsRoute route', () => { const req = requestMock.create({ method: 'get', path: RESULTS_ROUTE_PATH, - query: { patterns: 'logs-*,alerts-*' }, + query: { pattern: 'logs-*' }, }); beforeEach(() => { @@ -120,54 +128,69 @@ describe.skip('getResultsRoute route', () => { context.core.elasticsearch.client.asInternalUser.search.mockResolvedValue(searchResponse); - context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges.mockResolvedValue({ - index: { 'logs-*': { all: true }, 'alerts-*': { all: true } }, - } as unknown as SecurityHasPrivilegesResponse); + context.core.elasticsearch.client.asInternalUser.indices.get.mockResolvedValue({ + [resultDocument.indexName]: {}, + }); getResultsRoute(server.router, logger); }); - it('should authorize pattern', async () => { - const mockHasPrivileges = - context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges; - mockHasPrivileges.mockResolvedValueOnce({ - index: { 'logs-*': { all: true }, 'alerts-*': { all: true } }, - } as unknown as SecurityHasPrivilegesResponse); + it('should authorize indices from pattern', async () => { + const mockGetIndices = context.core.elasticsearch.client.asInternalUser.indices.get; + mockGetIndices.mockResolvedValueOnce({ [resultDocument.indexName]: {} }); const response = await server.inject(req, requestContextMock.convertContext(context)); - expect(mockHasPrivileges).toHaveBeenCalledWith({ - index: [ - { names: ['logs-*', 'alerts-*'], privileges: ['all', 'read', 'view_index_metadata'] }, - ], - }); + expect(mockGetIndices).toHaveBeenCalledWith({ index: 'logs-*', features: 'aliases' }); + expect(mockCheckIndicesPrivileges).toHaveBeenCalledWith( + expect.objectContaining({ indices: [resultDocument.indexName] }) + ); expect(context.core.elasticsearch.client.asInternalUser.search).toHaveBeenCalled(); expect(response.status).toEqual(200); - expect(response.body).toEqual([{ '@timestamp': expect.any(Number), ...resultBody }]); + expect(response.body).toEqual([resultDocument]); }); - it('should search authorized patterns only', async () => { - const mockHasPrivileges = - context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges; - mockHasPrivileges.mockResolvedValueOnce({ - index: { 'logs-*': { all: false }, 'alerts-*': { all: true } }, - } as unknown as SecurityHasPrivilegesResponse); + it('should authorize data streams from pattern', async () => { + const dataStreamName = 'test_data_stream_name'; + const resultIndexNameTwo = `${resultDocument.indexName}_2`; + const resultIndexNameThree = `${resultDocument.indexName}_3`; + const mockGetIndices = context.core.elasticsearch.client.asInternalUser.indices.get; + mockGetIndices.mockResolvedValueOnce({ + [resultDocument.indexName]: {}, + [resultIndexNameTwo]: { data_stream: dataStreamName }, + [resultIndexNameThree]: { data_stream: dataStreamName }, + }); const response = await server.inject(req, requestContextMock.convertContext(context)); + + expect(mockGetIndices).toHaveBeenCalledWith({ index: 'logs-*', features: 'aliases' }); + expect(mockCheckIndicesPrivileges).toHaveBeenCalledWith( + expect.objectContaining({ indices: [resultDocument.indexName, dataStreamName] }) + ); expect(context.core.elasticsearch.client.asInternalUser.search).toHaveBeenCalledWith({ index: expect.any(String), - ...getQuery(['alerts-*']), + ...getQuery([resultDocument.indexName, resultIndexNameTwo, resultIndexNameThree]), }); expect(response.status).toEqual(200); + expect(response.body).toEqual([resultDocument]); + }); + + it('should not search unknown indices', async () => { + const mockGetIndices = context.core.elasticsearch.client.asInternalUser.indices.get; + mockGetIndices.mockResolvedValueOnce({}); // empty object means no index is found + + const response = await server.inject(req, requestContextMock.convertContext(context)); + + expect(mockCheckIndicesPrivileges).not.toHaveBeenCalled(); + expect(context.core.elasticsearch.client.asInternalUser.search).not.toHaveBeenCalled(); + + expect(response.status).toEqual(200); + expect(response.body).toEqual([]); }); - it('should not search unauthorized patterns', async () => { - const mockHasPrivileges = - context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges; - mockHasPrivileges.mockResolvedValueOnce({ - index: { 'logs-*': { all: false }, 'alerts-*': { all: false } }, - } as unknown as SecurityHasPrivilegesResponse); + it('should not search unauthorized indices', async () => { + mockCheckIndicesPrivileges.mockResolvedValueOnce({}); // empty object means no index is authorized const response = await server.inject(req, requestContextMock.convertContext(context)); expect(context.core.elasticsearch.client.asInternalUser.search).not.toHaveBeenCalled(); @@ -176,11 +199,19 @@ describe.skip('getResultsRoute route', () => { expect(response.body).toEqual([]); }); - it('handles pattern authorization error', async () => { + it('handles index discovery error', async () => { + const errorMessage = 'Error!'; + const mockGetIndices = context.core.elasticsearch.client.asInternalUser.indices.get; + mockGetIndices.mockRejectedValueOnce({ message: errorMessage }); + + const response = await server.inject(req, requestContextMock.convertContext(context)); + expect(response.status).toEqual(500); + expect(response.body).toEqual({ message: errorMessage, status_code: 500 }); + }); + + it('handles index authorization error', async () => { const errorMessage = 'Error!'; - const mockHasPrivileges = - context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges; - mockHasPrivileges.mockRejectedValueOnce({ message: errorMessage }); + mockCheckIndicesPrivileges.mockRejectedValueOnce({ message: errorMessage }); const response = await server.inject(req, requestContextMock.convertContext(context)); expect(response.status).toEqual(500); diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.ts index 56729c7a40ab7..6c410e88f3626 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.ts @@ -11,20 +11,18 @@ import { RESULTS_ROUTE_PATH, INTERNAL_API_VERSION } from '../../../common/consta import { buildResponse } from '../../lib/build_response'; import { buildRouteValidation } from '../../schemas/common'; import { GetResultQuery } from '../../schemas/result'; -import type { Result, ResultDocument } from '../../schemas/result'; +import type { ResultDocument } from '../../schemas/result'; import { API_DEFAULT_ERROR_MESSAGE } from '../../translations'; import type { DataQualityDashboardRequestHandlerContext } from '../../types'; -import { createResultFromDocument } from './parser'; import { API_RESULTS_INDEX_NOT_AVAILABLE } from './translations'; +import { checkIndicesPrivileges } from './privileges'; -export const getQuery = (patterns: string[]) => ({ +export const getQuery = (indexName: string[]) => ({ size: 0, - query: { - bool: { filter: [{ terms: { 'rollup.pattern': patterns } }] }, - }, + query: { bool: { filter: [{ terms: { indexName } }] } }, aggs: { latest: { - terms: { field: 'rollup.pattern', size: 10000 }, // big enough to get all patterns, but under `index.max_terms_count` (default 65536) + terms: { field: 'indexName', size: 10000 }, // big enough to get all indexNames, but under `index.max_terms_count` (default 65536) aggs: { latest_doc: { top_hits: { size: 1, sort: [{ '@timestamp': { order: 'desc' } }] } } }, }, }, @@ -51,10 +49,6 @@ export const getResultsRoute = ( validate: { request: { query: buildRouteValidation(GetResultQuery) } }, }, async (context, request, response) => { - // TODO: https://github.com/elastic/kibana/pull/173185#issuecomment-1908034302 - return response.ok({ body: [] }); - - // eslint-disable-next-line no-unreachable const services = await context.resolve(['core', 'dataQualityDashboard']); const resp = buildResponse(response); @@ -70,38 +64,71 @@ export const getResultsRoute = ( } try { - // Confirm user has authorization for the requested patterns - const { patterns } = request.query; - const userEsClient = services.core.elasticsearch.client.asCurrentUser; - const privileges = await userEsClient.security.hasPrivileges({ - index: [ - { names: patterns.split(','), privileges: ['all', 'read', 'view_index_metadata'] }, - ], + const { client } = services.core.elasticsearch; + const { pattern } = request.query; + + // Discover all indices for the pattern using internal user + const indicesResponse = await client.asInternalUser.indices.get({ + index: pattern, + features: 'aliases', // omit 'settings' and 'mappings' to reduce response size }); - const authorizedPatterns = Object.keys(privileges.index).filter((pattern) => - Object.values(privileges.index[pattern]).some((v) => v === true) - ); - if (authorizedPatterns.length === 0) { + + // map data streams to their backing indices and collect indices to authorize + const indicesToAuthorize: string[] = []; + const dataStreamIndices: Record = {}; + Object.entries(indicesResponse).forEach(([indexName, { data_stream: dataStream }]) => { + if (dataStream) { + if (!dataStreamIndices[dataStream]) { + dataStreamIndices[dataStream] = []; + } + dataStreamIndices[dataStream].push(indexName); + } else { + indicesToAuthorize.push(indexName); + } + }); + indicesToAuthorize.push(...Object.keys(dataStreamIndices)); + if (indicesToAuthorize.length === 0) { return response.ok({ body: [] }); } - // Get the latest result of each pattern - const query = { index, ...getQuery(authorizedPatterns) }; - const internalEsClient = services.core.elasticsearch.client.asInternalUser; + // check privileges for indices or data streams + const hasIndexPrivileges = await checkIndicesPrivileges({ + client, + indices: indicesToAuthorize, + }); + + // filter out unauthorized indices, and expand data streams backing indices + const authorizedIndexNames = Object.entries(hasIndexPrivileges).reduce( + (acc, [indexName, authorized]) => { + if (authorized) { + if (dataStreamIndices[indexName]) { + acc.push(...dataStreamIndices[indexName]); + } else { + acc.push(indexName); + } + } + return acc; + }, + [] + ); + if (authorizedIndexNames.length === 0) { + return response.ok({ body: [] }); + } - const { aggregations } = await internalEsClient.search< + // Get the latest result for each indexName + const query = { index, ...getQuery(authorizedIndexNames) }; + const { aggregations } = await client.asInternalUser.search< ResultDocument, Record >(query); - const results: Result[] = - aggregations?.latest?.buckets.map((bucket) => - createResultFromDocument(bucket.latest_doc.hits.hits[0]._source) - ) ?? []; + const results: ResultDocument[] = + aggregations?.latest?.buckets.map((bucket) => bucket.latest_doc.hits.hits[0]._source) ?? + []; return response.ok({ body: results }); } catch (err) { - logger.error(JSON.stringify(err)); + logger.error(err.message); return resp.error({ body: err.message ?? API_DEFAULT_ERROR_MESSAGE, diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/parser.test.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/parser.test.ts deleted file mode 100644 index 56800801ffc8f..0000000000000 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/parser.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { createDocumentFromResult, createResultFromDocument } from './parser'; -import { resultBody, resultDocument } from './results.mock'; - -describe('createDocumentFromResult', () => { - it('should create document from result', () => { - const document = createDocumentFromResult(resultBody); - expect(document).toEqual({ ...resultDocument, '@timestamp': expect.any(Number) }); - }); -}); - -describe('createResultFromDocument', () => { - it('should create document from result', () => { - const result = createResultFromDocument(resultDocument); - expect(result).toEqual({ ...resultBody, '@timestamp': expect.any(Number) }); - }); -}); diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/parser.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/parser.ts deleted file mode 100644 index 198d5522839e4..0000000000000 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/parser.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 type { Result, ResultDocument, IndexArray, IndexObject } from '../../schemas/result'; - -export const createDocumentFromResult = (result: Result): ResultDocument => { - const { rollup } = result; - const document: ResultDocument = { - ...result, - '@timestamp': Date.now(), - rollup: { - ...rollup, - ilmExplain: indexObjectToIndexArray(rollup.ilmExplain), - stats: indexObjectToIndexArray(rollup.stats), - results: indexObjectToIndexArray(rollup.results), - }, - }; - - return document; -}; - -export const createResultFromDocument = (document: ResultDocument): Result => { - const { rollup } = document; - const result = { - ...document, - rollup: { - ...rollup, - ilmExplain: indexArrayToIndexObject(rollup.ilmExplain), - stats: indexArrayToIndexObject(rollup.stats), - results: indexArrayToIndexObject(rollup.results), - }, - }; - - return result; -}; - -// ES parses object keys containing `.` as nested dot-separated field names (e.g. `event.name`). -// we need to convert documents containing objects with "indexName" keys (e.g. `.index-name-checked`) -// to object arrays so they can be stored correctly, we keep the key in the `_indexName` field. -const indexObjectToIndexArray = (obj: IndexObject): IndexArray => - Object.entries(obj).map(([key, value]) => ({ ...value, _indexName: key })); - -// convert index arrays back to objects with indexName as key -const indexArrayToIndexObject = (arr: IndexArray): IndexObject => - Object.fromEntries(arr.map(({ _indexName, ...value }) => [_indexName, value])); diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/post_results.test.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/post_results.test.ts index 98eb67ecbaaa8..f3175a737ee54 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/post_results.test.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/post_results.test.ts @@ -11,20 +11,29 @@ import { requestMock } from '../../__mocks__/request'; import { requestContextMock } from '../../__mocks__/request_context'; import { postResultsRoute } from './post_results'; import { loggerMock, type MockedLogger } from '@kbn/logging-mocks'; -import type { - SecurityHasPrivilegesResponse, - WriteResponseBase, -} from '@elastic/elasticsearch/lib/api/types'; -import { resultBody, resultDocument } from './results.mock'; - -// TODO: https://github.com/elastic/kibana/pull/173185#issuecomment-1908034302 -describe.skip('postResultsRoute route', () => { +import type { WriteResponseBase } from '@elastic/elasticsearch/lib/api/types'; +import { resultDocument } from './results.mock'; +import type { CheckIndicesPrivilegesParam } from './privileges'; + +const mockCheckIndicesPrivileges = jest.fn(({ indices }: CheckIndicesPrivilegesParam) => + Promise.resolve(Object.fromEntries(indices.map((index) => [index, true]))) +); +jest.mock('./privileges', () => ({ + checkIndicesPrivileges: (params: CheckIndicesPrivilegesParam) => + mockCheckIndicesPrivileges(params), +})); + +describe('postResultsRoute route', () => { describe('indexation', () => { let server: ReturnType; let { context } = requestContextMock.createTools(); let logger: MockedLogger; - const req = requestMock.create({ method: 'post', path: RESULTS_ROUTE_PATH, body: resultBody }); + const req = requestMock.create({ + method: 'post', + path: RESULTS_ROUTE_PATH, + body: resultDocument, + }); beforeEach(() => { jest.clearAllMocks(); @@ -34,10 +43,9 @@ describe.skip('postResultsRoute route', () => { ({ context } = requestContextMock.createTools()); - context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges.mockResolvedValue({ - has_all_requested: true, - } as unknown as SecurityHasPrivilegesResponse); - + context.core.elasticsearch.client.asInternalUser.indices.get.mockResolvedValue({ + [resultDocument.indexName]: {}, + }); postResultsRoute(server.router, logger); }); @@ -80,12 +88,16 @@ describe.skip('postResultsRoute route', () => { }); }); - describe('request pattern authorization', () => { + describe('request index authorization', () => { let server: ReturnType; let { context } = requestContextMock.createTools(); let logger: MockedLogger; - const req = requestMock.create({ method: 'post', path: RESULTS_ROUTE_PATH, body: resultBody }); + const req = requestMock.create({ + method: 'post', + path: RESULTS_ROUTE_PATH, + body: resultDocument, + }); beforeEach(() => { jest.clearAllMocks(); @@ -95,6 +107,9 @@ describe.skip('postResultsRoute route', () => { ({ context } = requestContextMock.createTools()); + context.core.elasticsearch.client.asInternalUser.indices.get.mockResolvedValue({ + [resultDocument.indexName]: {}, + }); context.core.elasticsearch.client.asInternalUser.index.mockResolvedValueOnce({ result: 'created', } as WriteResponseBase); @@ -102,42 +117,41 @@ describe.skip('postResultsRoute route', () => { postResultsRoute(server.router, logger); }); - it('should authorize pattern', async () => { - const mockHasPrivileges = - context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges; - mockHasPrivileges.mockResolvedValueOnce({ - has_all_requested: true, - } as unknown as SecurityHasPrivilegesResponse); + it('should authorize index', async () => { + const response = await server.inject(req, requestContextMock.convertContext(context)); + expect(mockCheckIndicesPrivileges).toHaveBeenCalledWith({ + client: context.core.elasticsearch.client, + indices: [resultDocument.indexName], + }); + expect(context.core.elasticsearch.client.asInternalUser.index).toHaveBeenCalled(); + expect(response.status).toEqual(200); + expect(response.body).toEqual({ result: 'created' }); + }); + + it('should authorize data stream', async () => { + const dataStreamName = 'test_data_stream_name'; + context.core.elasticsearch.client.asInternalUser.indices.get.mockResolvedValue({ + [resultDocument.indexName]: { data_stream: dataStreamName }, + }); + mockCheckIndicesPrivileges.mockResolvedValueOnce({ [dataStreamName]: true }); const response = await server.inject(req, requestContextMock.convertContext(context)); - expect(mockHasPrivileges).toHaveBeenCalledWith({ - index: [ - { - names: [resultBody.rollup.pattern], - privileges: ['all', 'read', 'view_index_metadata'], - }, - ], + expect(mockCheckIndicesPrivileges).toHaveBeenCalledWith({ + client: context.core.elasticsearch.client, + indices: [dataStreamName], }); expect(context.core.elasticsearch.client.asInternalUser.index).toHaveBeenCalled(); expect(response.status).toEqual(200); expect(response.body).toEqual({ result: 'created' }); }); - it('should not index unauthorized pattern', async () => { - const mockHasPrivileges = - context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges; - mockHasPrivileges.mockResolvedValueOnce({ - has_all_requested: false, - } as unknown as SecurityHasPrivilegesResponse); + it('should not index unauthorized index', async () => { + mockCheckIndicesPrivileges.mockResolvedValueOnce({ [resultDocument.indexName]: false }); const response = await server.inject(req, requestContextMock.convertContext(context)); - expect(mockHasPrivileges).toHaveBeenCalledWith({ - index: [ - { - names: [resultBody.rollup.pattern], - privileges: ['all', 'read', 'view_index_metadata'], - }, - ], + expect(mockCheckIndicesPrivileges).toHaveBeenCalledWith({ + client: context.core.elasticsearch.client, + indices: [resultDocument.indexName], }); expect(context.core.elasticsearch.client.asInternalUser.index).not.toHaveBeenCalled(); @@ -145,11 +159,9 @@ describe.skip('postResultsRoute route', () => { expect(response.body).toEqual({ result: 'noop' }); }); - it('handles pattern authorization error', async () => { + it('handles index authorization error', async () => { const errorMessage = 'Error!'; - const mockHasPrivileges = - context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges; - mockHasPrivileges.mockRejectedValueOnce({ message: errorMessage }); + mockCheckIndicesPrivileges.mockRejectedValueOnce(Error(errorMessage)); const response = await server.inject(req, requestContextMock.convertContext(context)); expect(response.status).toEqual(500); @@ -170,7 +182,7 @@ describe.skip('postResultsRoute route', () => { const req = requestMock.create({ method: 'post', path: RESULTS_ROUTE_PATH, - body: { rollup: resultBody.rollup }, + body: { indexName: 'invalid body' }, }); const result = server.validate(req); diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/post_results.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/post_results.ts index 1162d23f1dfad..b4b2e4b219bc4 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/post_results.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/post_results.ts @@ -13,7 +13,7 @@ import { buildRouteValidation } from '../../schemas/common'; import { PostResultBody } from '../../schemas/result'; import { API_DEFAULT_ERROR_MESSAGE } from '../../translations'; import type { DataQualityDashboardRequestHandlerContext } from '../../types'; -import { createDocumentFromResult } from './parser'; +import { checkIndicesPrivileges } from './privileges'; import { API_RESULTS_INDEX_NOT_AVAILABLE } from './translations'; export const postResultsRoute = ( @@ -32,10 +32,6 @@ export const postResultsRoute = ( validate: { request: { body: buildRouteValidation(PostResultBody) } }, }, async (context, request, response) => { - // TODO: https://github.com/elastic/kibana/pull/173185#issuecomment-1908034302 - return response.ok({ body: { result: 'noop' } }); - - // eslint-disable-next-line no-unreachable const services = await context.resolve(['core', 'dataQualityDashboard']); const resp = buildResponse(response); @@ -51,24 +47,35 @@ export const postResultsRoute = ( } try { - // Confirm user has authorization for the pattern payload - const { pattern } = request.body.rollup; - const userEsClient = services.core.elasticsearch.client.asCurrentUser; - const privileges = await userEsClient.security.hasPrivileges({ - index: [{ names: [pattern], privileges: ['all', 'read', 'view_index_metadata'] }], + const { client } = services.core.elasticsearch; + const { indexName } = request.body; + + // Confirm index exists and get the data stream name if it's a data stream + const indicesResponse = await client.asInternalUser.indices.get({ + index: indexName, + features: 'aliases', + }); + if (!indicesResponse[indexName]) { + return response.ok({ body: { result: 'noop' } }); + } + const indexOrDataStream = indicesResponse[indexName].data_stream ?? indexName; + + // Confirm user has authorization for the index name or data stream + const hasIndexPrivileges = await checkIndicesPrivileges({ + client, + indices: [indexOrDataStream], }); - if (!privileges.has_all_requested) { + if (!hasIndexPrivileges[indexOrDataStream]) { return response.ok({ body: { result: 'noop' } }); } // Index the result - const document = createDocumentFromResult(request.body); - const esClient = services.core.elasticsearch.client.asInternalUser; - const outcome = await esClient.index({ index, body: document }); + const body = { '@timestamp': Date.now(), ...request.body }; + const outcome = await client.asInternalUser.index({ index, body }); return response.ok({ body: { result: outcome.result } }); } catch (err) { - logger.error(JSON.stringify(err)); + logger.error(err.message); return resp.error({ body: err.message ?? API_DEFAULT_ERROR_MESSAGE, diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/privileges.test.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/privileges.test.ts new file mode 100644 index 0000000000000..2833e3f030fdd --- /dev/null +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/privileges.test.ts @@ -0,0 +1,129 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SecurityHasPrivilegesResponse } from '@elastic/elasticsearch/lib/api/types'; +import { requestContextMock } from '../../__mocks__/request_context'; +import { checkIndicesPrivileges } from './privileges'; + +// const mockHasPrivileges = +// context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges; +// mockHasPrivileges.mockResolvedValueOnce({ +// has_all_requested: true, +// } as unknown as SecurityHasPrivilegesResponse); + +describe('checkIndicesPrivileges', () => { + const { context } = requestContextMock.createTools(); + const { client } = context.core.elasticsearch; + + beforeEach(() => { + client.asCurrentUser.security.hasPrivileges.mockResolvedValue({ + index: { + index1: { + read: true, + view_index_metadata: true, + manage: true, + monitor: true, + }, + index2: { + read: true, + view_index_metadata: true, + manage: true, + monitor: true, + }, + }, + } as unknown as SecurityHasPrivilegesResponse); + }); + + it('should return true if user has required privileges', async () => { + const result = await checkIndicesPrivileges({ client, indices: ['index1', 'index2'] }); + expect(result).toEqual({ index1: true, index2: true }); + }); + + it('should return true if only monitor privileges is missing', async () => { + client.asCurrentUser.security.hasPrivileges.mockResolvedValueOnce({ + index: { + index1: { + read: true, + view_index_metadata: true, + manage: true, + monitor: false, + }, + }, + } as unknown as SecurityHasPrivilegesResponse); + const result = await checkIndicesPrivileges({ client, indices: ['index1'] }); + + expect(result).toEqual({ index1: true }); + }); + + it('should return true if only manage privileges is missing', async () => { + client.asCurrentUser.security.hasPrivileges.mockResolvedValueOnce({ + index: { + index1: { + read: true, + view_index_metadata: true, + manage: false, + monitor: true, + }, + }, + } as unknown as SecurityHasPrivilegesResponse); + + const result = await checkIndicesPrivileges({ client, indices: ['index1'] }); + + expect(result).toEqual({ index1: true }); + }); + + it('should return false if both manage and monitor privileges is missing', async () => { + client.asCurrentUser.security.hasPrivileges.mockResolvedValueOnce({ + index: { + index1: { + read: true, + view_index_metadata: true, + manage: false, + monitor: false, + }, + }, + } as unknown as SecurityHasPrivilegesResponse); + + const result = await checkIndicesPrivileges({ client, indices: ['index1'] }); + + expect(result).toEqual({ index1: false }); + }); + + it('should return false if only read privilege is missing', async () => { + client.asCurrentUser.security.hasPrivileges.mockResolvedValueOnce({ + index: { + index1: { + read: false, + view_index_metadata: true, + manage: true, + monitor: true, + }, + }, + } as unknown as SecurityHasPrivilegesResponse); + + const result = await checkIndicesPrivileges({ client, indices: ['index1'] }); + + expect(result).toEqual({ index1: false }); + }); + + it('should return false if only view_index_metadata privilege is missing', async () => { + client.asCurrentUser.security.hasPrivileges.mockResolvedValueOnce({ + index: { + index1: { + read: true, + view_index_metadata: false, + manage: true, + monitor: true, + }, + }, + } as unknown as SecurityHasPrivilegesResponse); + + const result = await checkIndicesPrivileges({ client, indices: ['index1'] }); + + expect(result).toEqual({ index1: false }); + }); +}); diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/privileges.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/privileges.ts new file mode 100644 index 0000000000000..ebda2f54e16e0 --- /dev/null +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/privileges.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { IScopedClusterClient } from '@kbn/core-elasticsearch-server'; + +export interface CheckIndicesPrivilegesParam { + client: IScopedClusterClient; + indices: string[]; +} + +/** + * Checks user has the required privileges to do a results check for the given indices. + * In order to be allowed to do a result check user needs: + * `read`, `view_index_metadata` and (`monitor` or `manage`) index privileges. + */ +export const checkIndicesPrivileges = async ({ client, indices }: CheckIndicesPrivilegesParam) => { + const privileges = await client.asCurrentUser.security.hasPrivileges({ + index: [{ names: indices, privileges: ['read', 'view_index_metadata', 'monitor', 'manage'] }], + }); + + const hasRequiredIndexPrivilege: Record = {}; + Object.entries(privileges.index).forEach( + ([indexName, { read, view_index_metadata: viewMetadata, monitor, manage }]) => { + hasRequiredIndexPrivilege[indexName] = read && viewMetadata && (monitor || manage); + } + ); + + return hasRequiredIndexPrivilege; +}; diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/results.mock.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/results.mock.ts index 1d0b15a4c24c0..36ca3d2dc4e66 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/results.mock.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/results.mock.ts @@ -8,195 +8,29 @@ import type { ResultDocument } from '../../schemas/result'; export const resultDocument: ResultDocument = { - '@timestamp': 1622767273955, - meta: { - batchId: 'aae36cd8-3825-4ad1-baa4-79bdf4617f8a', - ecsVersion: '8.6.1', - errorCount: 0, - ilmPhase: 'hot', - indexId: 'aO29KOwtQ3Snf-Pit5Wf4w', - indexName: '.internal.alerts-security.alerts-default-000001', - isCheckAll: true, - numberOfDocuments: 20, - numberOfFields: 1726, - numberOfIncompatibleFields: 2, - numberOfEcsFields: 1440, - numberOfCustomFields: 284, - numberOfIndices: 1, - numberOfIndicesChecked: 1, - numberOfSameFamily: 0, - sameFamilyFields: [], - sizeInBytes: 506471, - timeConsumedMs: 85, - unallowedMappingFields: [], - unallowedValueFields: ['event.category', 'event.outcome'], - }, - rollup: { - docsCount: 20, - error: null, - ilmExplain: [ - { - _indexName: '.internal.alerts-security.alerts-default-000001', - index: '.internal.alerts-security.alerts-default-000001', - managed: true, - policy: '.alerts-ilm-policy', - index_creation_date_millis: 1700757268526, - time_since_index_creation: '20.99d', - lifecycle_date_millis: 1700757268526, - age: '20.99d', - phase: 'hot', - phase_time_millis: 1700757270294, - action: 'rollover', - action_time_millis: 1700757273955, - step: 'check-rollover-ready', - step_time_millis: 1700757273955, - phase_execution: { - policy: '.alerts-ilm-policy', - phase_definition: { - min_age: '0ms', - actions: { - rollover: { - max_age: '30d', - max_primary_shard_size: '50gb', - }, - }, - }, - version: 1, - modified_date_in_millis: 1700757266723, - }, - }, - ], - ilmExplainPhaseCounts: { - hot: 1, - warm: 0, - cold: 0, - frozen: 0, - unmanaged: 0, - }, - indices: 1, - pattern: '.alerts-security.alerts-default', - results: [ - { - _indexName: '.internal.alerts-security.alerts-default-000001', - docsCount: 20, - error: null, - ilmPhase: 'hot', - incompatible: 2, - indexName: '.internal.alerts-security.alerts-default-000001', - markdownComments: [ - '### .internal.alerts-security.alerts-default-000001\n', - '| Result | Index | Docs | Incompatible fields | ILM Phase | Size |\n|--------|-------|------|---------------------|-----------|------|\n| ❌ | .internal.alerts-security.alerts-default-000001 | 20 (100,0 %) | 2 | `hot` | 494.6KB |\n\n', - '### **Incompatible fields** `2` **Same family** `0` **Custom fields** `284` **ECS compliant fields** `1440` **All fields** `1726`\n', - "#### 2 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.6.1.\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\n#### Incompatible field values - .internal.alerts-security.alerts-default-000001\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` | `behavior` (1) |\n| event.outcome | `failure`, `success`, `unknown` | `` (12) |\n\n', - ], - pattern: '.alerts-security.alerts-default', - sameFamily: 0, - }, - ], - sizeInBytes: 506471, - stats: [ - { - _indexName: '.internal.alerts-security.alerts-default-000001', - uuid: 'aO29KOwtQ3Snf-Pit5Wf4w', - health: 'green', - status: 'open', - }, - ], - }, -}; - -export const resultBody = { - meta: { - batchId: 'aae36cd8-3825-4ad1-baa4-79bdf4617f8a', - ecsVersion: '8.6.1', - errorCount: 0, - ilmPhase: 'hot', - indexId: 'aO29KOwtQ3Snf-Pit5Wf4w', - indexName: '.internal.alerts-security.alerts-default-000001', - isCheckAll: true, - numberOfDocuments: 20, - numberOfFields: 1726, - numberOfIncompatibleFields: 2, - numberOfEcsFields: 1440, - numberOfCustomFields: 284, - numberOfIndices: 1, - numberOfIndicesChecked: 1, - numberOfSameFamily: 0, - sameFamilyFields: [], - sizeInBytes: 506471, - timeConsumedMs: 85, - unallowedMappingFields: [], - unallowedValueFields: ['event.category', 'event.outcome'], - }, - rollup: { - docsCount: 20, - error: null, - ilmExplain: { - '.internal.alerts-security.alerts-default-000001': { - index: '.internal.alerts-security.alerts-default-000001', - managed: true, - policy: '.alerts-ilm-policy', - index_creation_date_millis: 1700757268526, - time_since_index_creation: '20.99d', - lifecycle_date_millis: 1700757268526, - age: '20.99d', - phase: 'hot', - phase_time_millis: 1700757270294, - action: 'rollover', - action_time_millis: 1700757273955, - step: 'check-rollover-ready', - step_time_millis: 1700757273955, - phase_execution: { - policy: '.alerts-ilm-policy', - phase_definition: { - min_age: '0ms', - actions: { - rollover: { - max_age: '30d', - max_primary_shard_size: '50gb', - }, - }, - }, - version: 1, - modified_date_in_millis: 1700757266723, - }, - }, - }, - ilmExplainPhaseCounts: { - hot: 1, - warm: 0, - cold: 0, - frozen: 0, - unmanaged: 0, - }, - indices: 1, - pattern: '.alerts-security.alerts-default', - results: { - '.internal.alerts-security.alerts-default-000001': { - docsCount: 20, - error: null, - ilmPhase: 'hot', - incompatible: 2, - indexName: '.internal.alerts-security.alerts-default-000001', - markdownComments: [ - '### .internal.alerts-security.alerts-default-000001\n', - '| Result | Index | Docs | Incompatible fields | ILM Phase | Size |\n|--------|-------|------|---------------------|-----------|------|\n| ❌ | .internal.alerts-security.alerts-default-000001 | 20 (100,0 %) | 2 | `hot` | 494.6KB |\n\n', - '### **Incompatible fields** `2` **Same family** `0` **Custom fields** `284` **ECS compliant fields** `1440` **All fields** `1726`\n', - "#### 2 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.6.1.\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\n#### Incompatible field values - .internal.alerts-security.alerts-default-000001\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` | `behavior` (1) |\n| event.outcome | `failure`, `success`, `unknown` | `` (12) |\n\n', - ], - pattern: '.alerts-security.alerts-default', - sameFamily: 0, - }, - }, - sizeInBytes: 506471, - stats: { - '.internal.alerts-security.alerts-default-000001': { - uuid: 'aO29KOwtQ3Snf-Pit5Wf4w', - health: 'green', - status: 'open', - }, - }, - }, + batchId: '33d95427-1fd3-43c3-bdeb-74324533a31e', + indexName: '.ds-logs-endpoint.alerts-default-2023.11.23-000001', + isCheckAll: false, + checkedAt: 1706526408000, + docsCount: 100, + totalFieldCount: 1582, + ecsFieldCount: 677, + customFieldCount: 904, + incompatibleFieldCount: 1, + sameFamilyFieldCount: 0, + sameFamilyFields: [], + unallowedMappingFields: [], + unallowedValueFields: ['event.category'], + sizeInBytes: 173796, + ilmPhase: 'hot', + markdownComments: [ + '### .ds-logs-endpoint.alerts-default-2023.11.23-000001\n', + '| Result | Index | Docs | Incompatible fields | ILM Phase | Size |\n|--------|-------|------|---------------------|-----------|------|\n| ❌ | .ds-logs-endpoint.alerts-default-2023.11.23-000001 | 100 (64,1 %) | 1 | `hot` | 274.6KB |\n\n', + '### **Incompatible fields** `1` **Same family** `0` **Custom fields** `904` **ECS compliant fields** `677` **All fields** `1582`\n', + "#### 1 incompatible field\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.6.1.\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\n#### Incompatible field values - .ds-logs-endpoint.alerts-default-2023.11.23-000001\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` | `behavior` (6) |\n\n', + ], + ecsVersion: '8.6.1', + indexId: 'PMhntcuPQ_yhPoNsXiM_hg', + error: null, }; diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/schemas/result.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/schemas/result.ts index 09851c9b8dc86..69387ea6fd8cf 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/schemas/result.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/schemas/result.ts @@ -7,64 +7,30 @@ import * as t from 'io-ts'; -export const ResultMeta = t.type({ +export const ResultDocument = t.type({ batchId: t.string, - ecsVersion: t.string, - errorCount: t.number, - ilmPhase: t.string, - indexId: t.string, indexName: t.string, isCheckAll: t.boolean, - numberOfDocuments: t.number, - numberOfFields: t.number, - numberOfIncompatibleFields: t.number, - numberOfEcsFields: t.number, - numberOfCustomFields: t.number, - numberOfIndices: t.number, - numberOfIndicesChecked: t.number, - numberOfSameFamily: t.number, + checkedAt: t.number, + docsCount: t.number, + totalFieldCount: t.number, + ecsFieldCount: t.number, + customFieldCount: t.number, + incompatibleFieldCount: t.number, + sameFamilyFieldCount: t.number, sameFamilyFields: t.array(t.string), - sizeInBytes: t.number, - timeConsumedMs: t.number, unallowedMappingFields: t.array(t.string), unallowedValueFields: t.array(t.string), -}); -export type ResultMeta = t.TypeOf; - -export const ResultRollup = t.type({ - docsCount: t.number, - error: t.union([t.string, t.null]), - indices: t.number, - pattern: t.string, sizeInBytes: t.number, - ilmExplainPhaseCounts: t.record(t.string, t.number), - ilmExplain: t.record(t.string, t.UnknownRecord), - stats: t.record(t.string, t.UnknownRecord), - results: t.record(t.string, t.UnknownRecord), -}); -export type ResultRollup = t.TypeOf; - -export const Result = t.type({ - meta: ResultMeta, - rollup: ResultRollup, + ilmPhase: t.string, + markdownComments: t.array(t.string), + ecsVersion: t.string, + indexId: t.string, + error: t.union([t.string, t.null]), }); -export type Result = t.TypeOf; - -export type IndexArray = Array<{ _indexName: string } & Record>; -export type IndexObject = Record>; +export type ResultDocument = t.TypeOf; -export type ResultDocument = Omit & { - '@timestamp': number; - rollup: Omit & { - stats: IndexArray; - results: IndexArray; - ilmExplain: IndexArray; - }; -}; +export const PostResultBody = ResultDocument; -// Routes validation schemas - -export const GetResultQuery = t.type({ patterns: t.string }); +export const GetResultQuery = t.type({ pattern: t.string }); export type GetResultQuery = t.TypeOf; - -export const PostResultBody = Result; diff --git a/x-pack/plugins/ecs_data_quality_dashboard/tsconfig.json b/x-pack/plugins/ecs_data_quality_dashboard/tsconfig.json index 04a7d2bf092f5..b725beec802b2 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/tsconfig.json +++ b/x-pack/plugins/ecs_data_quality_dashboard/tsconfig.json @@ -24,6 +24,7 @@ "@kbn/data-stream-adapter", "@kbn/spaces-plugin", "@kbn/core-elasticsearch-server-mocks", + "@kbn/core-elasticsearch-server", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/fetch_connector_by_id_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/fetch_connector_by_id_logic.ts new file mode 100644 index 0000000000000..320760e49ee72 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/fetch_connector_by_id_logic.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Connector } from '@kbn/search-connectors'; + +import { createApiLogic, Actions } from '../../../shared/api_logic/create_api_logic'; +import { HttpLogic } from '../../../shared/http'; + +export interface FetchConnectorByIdApiLogicArgs { + connectorId: string; +} +export interface FetchConnectorByIdApiLogicResponse { + connector: Connector | undefined; +} + +export const fetchConnectorById = async ({ + connectorId, +}: FetchConnectorByIdApiLogicArgs): Promise => { + const route = `/internal/enterprise_search/connectors/${connectorId}`; + const response = await HttpLogic.values.http.get(route); + return response; +}; + +export const FetchConnectorByIdApiLogic = createApiLogic( + ['fetch_connector_by_id_api_logic'], + fetchConnectorById +); + +export type FetchConnectorByIdApiLogicActions = Actions< + FetchConnectorByIdApiLogicArgs, + FetchConnectorByIdApiLogicResponse +>; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_detail.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_detail.tsx new file mode 100644 index 0000000000000..a3f77406750e9 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_detail.tsx @@ -0,0 +1,236 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect } from 'react'; +import { useParams } from 'react-router-dom'; + +import { useActions, useValues } from 'kea'; + +import { i18n } from '@kbn/i18n'; + +import { generateEncodedPath } from '../../../shared/encode_path_params'; +import { KibanaLogic } from '../../../shared/kibana'; +import { CONNECTOR_DETAIL_TAB_PATH } from '../../routes'; +import { baseBreadcrumbs } from '../connectors/connectors'; +import { EnterpriseSearchContentPageTemplate } from '../layout/page_template'; + +import { getHeaderActions } from '../search_index/components/header_actions/header_actions'; +import { ConnectorConfiguration } from '../search_index/connector/connector_configuration'; +import { ConnectorSchedulingComponent } from '../search_index/connector/connector_scheduling'; +import { ConnectorSyncRules } from '../search_index/connector/sync_rules/connector_rules'; +import { SearchIndexDocuments } from '../search_index/documents'; +import { SearchIndexIndexMappings } from '../search_index/index_mappings'; +import { SearchIndexPipelines } from '../search_index/pipelines/pipelines'; + +import { ConnectorViewLogic } from './connector_view_logic'; +import { ConnectorDetailOverview } from './overview'; + +export enum ConnectorDetailTabId { + // all indices + OVERVIEW = 'overview', + DOCUMENTS = 'documents', + INDEX_MAPPINGS = 'index_mappings', + PIPELINES = 'pipelines', + // connector indices + CONFIGURATION = 'configuration', + SYNC_RULES = 'sync_rules', + SCHEDULING = 'scheduling', +} + +export const ConnectorDetail: React.FC = () => { + const connectorId = decodeURIComponent(useParams<{ connectorId: string }>().connectorId); + const { hasFilteringFeature, isLoading, index, connector } = useValues(ConnectorViewLogic); + const { fetchConnector } = useActions(ConnectorViewLogic); + useEffect(() => { + fetchConnector({ connectorId }); + }, []); + + const { tabId = ConnectorDetailTabId.OVERVIEW } = useParams<{ + tabId?: string; + }>(); + + const { + productAccess: { hasAppSearchAccess }, + productFeatures: { hasDefaultIngestPipeline }, + } = useValues(KibanaLogic); + + const ALL_INDICES_TABS = [ + { + content: , + id: ConnectorDetailTabId.OVERVIEW, + isSelected: tabId === ConnectorDetailTabId.OVERVIEW, + label: i18n.translate( + 'xpack.enterpriseSearch.content.connectors.connectorDetail.overviewTabLabel', + { + defaultMessage: 'Overview', + } + ), + onClick: () => + KibanaLogic.values.navigateToUrl( + generateEncodedPath(CONNECTOR_DETAIL_TAB_PATH, { + connectorId, + tabId: ConnectorDetailTabId.OVERVIEW, + }) + ), + }, + { + content: , + disabled: !index, + id: ConnectorDetailTabId.DOCUMENTS, + isSelected: tabId === ConnectorDetailTabId.DOCUMENTS, + label: i18n.translate( + 'xpack.enterpriseSearch.content.connectors.connectorDetail.documentsTabLabel', + { + defaultMessage: 'Documents', + } + ), + onClick: () => + KibanaLogic.values.navigateToUrl( + generateEncodedPath(CONNECTOR_DETAIL_TAB_PATH, { + connectorId, + tabId: ConnectorDetailTabId.DOCUMENTS, + }) + ), + }, + { + content: , + disabled: !index, + id: ConnectorDetailTabId.INDEX_MAPPINGS, + isSelected: tabId === ConnectorDetailTabId.INDEX_MAPPINGS, + label: i18n.translate( + 'xpack.enterpriseSearch.content.connectors.connectorDetail.indexMappingsTabLabel', + { + defaultMessage: 'Index mappings', + } + ), + onClick: () => + KibanaLogic.values.navigateToUrl( + generateEncodedPath(CONNECTOR_DETAIL_TAB_PATH, { + connectorId, + tabId: ConnectorDetailTabId.INDEX_MAPPINGS, + }) + ), + }, + ]; + + const CONNECTOR_TABS = [ + { + content: , + id: ConnectorDetailTabId.CONFIGURATION, + isSelected: tabId === ConnectorDetailTabId.CONFIGURATION, + label: i18n.translate( + 'xpack.enterpriseSearch.content.connectors.connectorDetail.configurationTabLabel', + { + defaultMessage: 'Configuration', + } + ), + onClick: () => + KibanaLogic.values.navigateToUrl( + generateEncodedPath(CONNECTOR_DETAIL_TAB_PATH, { + connectorId, + tabId: ConnectorDetailTabId.CONFIGURATION, + }) + ), + }, + ...(hasFilteringFeature + ? [ + { + content: , + disabled: !index, + id: ConnectorDetailTabId.SYNC_RULES, + isSelected: tabId === ConnectorDetailTabId.SYNC_RULES, + label: i18n.translate( + 'xpack.enterpriseSearch.content.connectors.connectorDetail.syncRulesTabLabel', + { + defaultMessage: 'Sync rules', + } + ), + onClick: () => + KibanaLogic.values.navigateToUrl( + generateEncodedPath(CONNECTOR_DETAIL_TAB_PATH, { + connectorId, + tabId: ConnectorDetailTabId.SYNC_RULES, + }) + ), + }, + ] + : []), + { + content: , + disabled: !index, + id: ConnectorDetailTabId.SCHEDULING, + isSelected: tabId === ConnectorDetailTabId.SCHEDULING, + label: i18n.translate( + 'xpack.enterpriseSearch.content.connectors.connectorDetail.schedulingTabLabel', + { + defaultMessage: 'Scheduling', + } + ), + onClick: () => + KibanaLogic.values.navigateToUrl( + generateEncodedPath(CONNECTOR_DETAIL_TAB_PATH, { + connectorId, + tabId: ConnectorDetailTabId.SCHEDULING, + }) + ), + }, + ]; + + const PIPELINES_TAB = { + content: , + disabled: !index, + id: ConnectorDetailTabId.PIPELINES, + isSelected: tabId === ConnectorDetailTabId.PIPELINES, + label: i18n.translate( + 'xpack.enterpriseSearch.content.connectors.connectorDetail.pipelinesTabLabel', + { + defaultMessage: 'Pipelines', + } + ), + onClick: () => + KibanaLogic.values.navigateToUrl( + generateEncodedPath(CONNECTOR_DETAIL_TAB_PATH, { + connectorId, + tabId: ConnectorDetailTabId.PIPELINES, + }) + ), + }; + + interface TabMenuItem { + content: JSX.Element; + disabled?: boolean; + id: string; + label: string; + onClick?: () => void; + prepend?: React.ReactNode; + route?: string; + testSubj?: string; + } + + const tabs: TabMenuItem[] = [ + ...ALL_INDICES_TABS, + ...CONNECTOR_TABS, + ...(hasDefaultIngestPipeline ? [PIPELINES_TAB] : []), + ]; + + const selectedTab = tabs.find((tab) => tab.id === tabId); + + return ( + + {selectedTab?.content || null} + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_detail_router.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_detail_router.tsx new file mode 100644 index 0000000000000..539d549f2bd51 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_detail_router.tsx @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect } from 'react'; + +import { useActions } from 'kea'; + +import { Routes, Route } from '@kbn/shared-ux-router'; + +import { CONNECTOR_DETAIL_PATH, CONNECTOR_DETAIL_TAB_PATH } from '../../routes'; + +import { IndexNameLogic } from '../search_index/index_name_logic'; + +import { IndexViewLogic } from '../search_index/index_view_logic'; + +import { ConnectorDetail } from './connector_detail'; +import { ConnectorViewLogic } from './connector_view_logic'; + +export const ConnectorDetailRouter: React.FC = () => { + const { stopFetchIndexPoll } = useActions(IndexViewLogic); + useEffect(() => { + const unmountName = IndexNameLogic.mount(); + const unmountView = ConnectorViewLogic.mount(); + const unmountIndexView = IndexViewLogic.mount(); + return () => { + stopFetchIndexPoll(); + unmountName(); + unmountView(); + unmountIndexView(); + }; + }, []); + return ( + + + + + + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_stats.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_stats.tsx new file mode 100644 index 0000000000000..89f75d979984e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_stats.tsx @@ -0,0 +1,235 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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, { ReactNode } from 'react'; + +import { + EuiBadge, + EuiFlexGrid, + EuiFlexGroup, + EuiFlexItem, + EuiHealth, + EuiIcon, + EuiSplitPanel, + EuiText, + EuiTitle, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { Connector } from '@kbn/search-connectors'; + +import { ConnectorIndex } from '../../../../../common/types/indices'; + +import { generateEncodedPath } from '../../../shared/encode_path_params'; +import { EuiLinkTo } from '../../../shared/react_router_helpers'; +import { CONNECTOR_DETAIL_TAB_PATH } from '../../routes'; +import { + connectorStatusToColor, + connectorStatusToText, +} from '../../utils/connector_status_helpers'; + +import { CONNECTORS } from '../search_index/connector/constants'; + +import { ConnectorDetailTabId } from './connector_detail'; + +export interface ConnectorStatsProps { + connector: Connector; + indexData?: ConnectorIndex; +} + +export interface StatCardProps { + content: ReactNode; + footer: ReactNode; + title: string; +} + +export const StatCard: React.FC = ({ title, content, footer }) => { + return ( + + + + + +

{title}

+
+
+ {content} +
+
+ + {footer} + +
+ ); +}; + +export const ConnectorStats: React.FC = ({ connector, indexData }) => { + const connectorDefinition = CONNECTORS.find((c) => c.serviceType === connector.service_type); + return ( + + + + + + {connectorDefinition && connectorDefinition.icon && ( + + + + )} + + +

{connectorDefinition?.name ?? '-'}

+
+
+
+
+ + + {connectorStatusToText(connector?.status)} + + + + } + footer={ + + + + + + + + +

+ {i18n.translate( + 'xpack.enterpriseSearch.connectors.connectorStats.p.DocumentsLabel', + { + defaultMessage: '{documentAmount} Documents', + values: { + documentAmount: indexData?.total.docs.count ?? '-', + }, + } + )} +

+
+
+
+
+ + + + {i18n.translate( + 'xpack.enterpriseSearch.connectors.connectorStats.seeDocumentsTextLabel', + { + defaultMessage: 'See documents', + } + )} + + + +
+ } + /> +
+ + + + {connector.index_name} + + + + + + ) : ( + i18n.translate('xpack.enterpriseSearch.connectors.connectorStats.noIndex', { + defaultMessage: 'No index related', + }) + ) + } + footer={ + + + + + {i18n.translate( + 'xpack.enterpriseSearch.connectors.connectorStats.configureLink', + { + defaultMessage: 'Configure', + } + )} + + + + + } + /> + + + + + {connector.pipeline.name} + + + ) : ( + i18n.translate('xpack.enterpriseSearch.connectors.connectorStats.noPipelineText', { + defaultMessage: 'None', + }) + ) + } + footer={ + + + + + {i18n.translate( + 'xpack.enterpriseSearch.connectors.connectorStats.managePipelines', + { + defaultMessage: 'Manage pipelines', + } + )} + + + + + } + /> + +
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_view_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_view_logic.ts new file mode 100644 index 0000000000000..31e41eef9c969 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_view_logic.ts @@ -0,0 +1,176 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { kea, MakeLogicType } from 'kea'; + +import { Connector, FeatureName, IngestPipelineParams } from '@kbn/search-connectors'; + +import { Status } from '../../../../../common/types/api'; + +import { + FetchConnectorByIdApiLogic, + FetchConnectorByIdApiLogicActions, +} from '../../api/connector/fetch_connector_by_id_logic'; + +import { FetchIndexActions, FetchIndexApiLogic } from '../../api/index/fetch_index_api_logic'; +import { ElasticsearchViewIndex, IngestionMethod, IngestionStatus } from '../../types'; +import { IndexNameActions, IndexNameLogic } from '../search_index/index_name_logic'; + +export interface ConnectorViewActions { + fetchConnector: FetchConnectorByIdApiLogicActions['makeRequest']; + fetchConnectorApiError: FetchConnectorByIdApiLogicActions['apiError']; + fetchConnectorApiSuccess: FetchConnectorByIdApiLogicActions['apiSuccess']; + fetchIndex: FetchIndexActions['makeRequest']; + fetchIndexApiError: FetchIndexActions['apiError']; + fetchIndexApiSuccess: FetchIndexActions['apiSuccess']; + setIndexName: IndexNameActions['setIndexName']; +} + +// TODO UPDATE +export interface ConnectorViewValues { + connector: Connector | undefined; + connectorData: typeof FetchConnectorByIdApiLogic.values.data; + connectorError: string | undefined; + connectorId: string | null; + connectorName: string | null; + error: string | undefined; + fetchConnectorApiStatus: Status; + fetchIndexApiStatus: Status; + hasAdvancedFilteringFeature: boolean; + hasBasicFilteringFeature: boolean; + hasDocumentLevelSecurityFeature: boolean; + hasFilteringFeature: boolean; + hasIncrementalSyncFeature: boolean; + htmlExtraction: boolean | undefined; + index: ElasticsearchViewIndex | undefined; + indexName: string; + ingestionMethod: IngestionMethod; + ingestionStatus: IngestionStatus; + isCanceling: boolean; + isHiddenIndex: boolean; + isLoading: boolean; + isSyncing: boolean; + isWaitingForSync: boolean; + lastUpdated: string | null; + pipelineData: IngestPipelineParams | undefined; + recheckIndexLoading: boolean; + syncTriggeredLocally: boolean; // holds local value after update so UI updates correctly +} + +export const ConnectorViewLogic = kea>({ + actions: {}, + connect: { + actions: [ + IndexNameLogic, + ['setIndexName'], + FetchConnectorByIdApiLogic, + [ + 'makeRequest as fetchConnector', + 'apiSuccess as fetchConnectorApiSuccess', + 'apiError as fetchConnectorApiError', + ], + FetchIndexApiLogic, + [ + 'makeRequest as fetchIndex', + 'apiSuccess as fetchIndexApiSuccess', + 'apiError as fetchIndexApiError', + ], + ], + values: [ + FetchConnectorByIdApiLogic, + ['status as fetchConnectorApiStatus', 'data as connectorData'], + FetchIndexApiLogic, + ['data as index', 'status as fetchIndexApiStatus'], + ], + }, + listeners: ({ actions, values }) => ({ + fetchConnectorApiSuccess: () => { + if (values.indexName) { + actions.fetchIndex({ indexName: values.indexName }); + actions.setIndexName(values.indexName); + } + }, + }), + path: ['enterprise_search', 'content', 'connector_view_logic'], + reducers: { + syncTriggeredLocally: [ + false, + { + fetchIndexApiSuccess: () => false, + startSyncApiSuccess: () => true, + }, + ], + }, + selectors: ({ selectors }) => ({ + connector: [ + () => [selectors.connectorData], + (connectorData) => { + return connectorData?.connector; + }, + ], + indexName: [ + () => [selectors.connector], + (connector: Connector | undefined) => { + return connector?.index_name || undefined; + }, + ], + isLoading: [ + () => [selectors.fetchConnectorApiStatus, selectors.fetchIndexApiStatus], + (fetchConnectorApiStatus: Status, fetchIndexApiStatus: Status) => + [Status.IDLE && Status.LOADING].includes(fetchConnectorApiStatus) || + [Status.IDLE && Status.LOADING].includes(fetchIndexApiStatus), + ], + connectorId: [() => [selectors.connector], (connector) => connector?.id], + connectorError: [ + () => [selectors.connector], + (connector: Connector | undefined) => connector?.error, + ], + error: [ + () => [selectors.connector], + (connector: Connector | undefined) => connector?.error || connector?.last_sync_error || null, + ], + hasAdvancedFilteringFeature: [ + () => [selectors.connector], + (connector?: Connector) => + connector?.features + ? connector.features[FeatureName.SYNC_RULES]?.advanced?.enabled ?? + connector.features[FeatureName.FILTERING_ADVANCED_CONFIG] + : false, + ], + hasBasicFilteringFeature: [ + () => [selectors.connector], + (connector?: Connector) => + connector?.features + ? connector.features[FeatureName.SYNC_RULES]?.basic?.enabled ?? + connector.features[FeatureName.FILTERING_RULES] + : false, + ], + hasDocumentLevelSecurityFeature: [ + () => [selectors.connector], + (connector?: Connector) => + connector?.features?.[FeatureName.DOCUMENT_LEVEL_SECURITY]?.enabled || false, + ], + hasFilteringFeature: [ + () => [selectors.hasAdvancedFilteringFeature, selectors.hasBasicFilteringFeature], + (advancedFeature: boolean, basicFeature: boolean) => advancedFeature || basicFeature, + ], + hasIncrementalSyncFeature: [ + () => [selectors.connector], + (connector?: Connector) => + connector?.features?.[FeatureName.INCREMENTAL_SYNC]?.enabled || false, + ], + htmlExtraction: [ + () => [selectors.connector], + (connector: Connector | undefined) => + connector?.configuration.extract_full_html?.value ?? undefined, + ], + pipelineData: [ + () => [selectors.connector], + (connector: Connector | undefined) => connector?.pipeline ?? undefined, + ], + }), +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/overview.logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/overview.logic.ts new file mode 100644 index 0000000000000..790cd87f9b4e3 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/overview.logic.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 { kea, MakeLogicType } from 'kea'; + +import { Status } from '../../../../../common/types/api'; +import { KibanaLogic } from '../../../shared/kibana'; + +import { + CachedFetchIndexApiLogic, + CachedFetchIndexApiLogicActions, +} from '../../api/index/cached_fetch_index_api_logic'; + +import { CONNECTORS_PATH } from '../../routes'; + +interface OverviewLogicActions { + apiError: CachedFetchIndexApiLogicActions['apiError']; +} + +interface OverviewLogicValues { + apiKey: string; + indexData: typeof CachedFetchIndexApiLogic.values.indexData; + isError: boolean; + isLoading: boolean; + isManageKeysPopoverOpen: boolean; + status: typeof CachedFetchIndexApiLogic.values.status; +} + +export const OverviewLogic = kea>({ + connect: { + actions: [CachedFetchIndexApiLogic, ['apiError']], + values: [CachedFetchIndexApiLogic, ['indexData', 'status']], + }, + listeners: () => ({ + apiError: async (_, breakpoint) => { + // show error for a second before navigating away + await breakpoint(1000); + KibanaLogic.values.navigateToUrl(CONNECTORS_PATH); + }, + }), + path: ['enterprise_search', 'connector_detail', 'overview'], + selectors: ({ selectors }) => ({ + isError: [() => [selectors.status], (status) => status === Status.ERROR], + }), +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/overview.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/overview.tsx new file mode 100644 index 0000000000000..3e9a4af5ee3ad --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/overview.tsx @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useActions, useValues } from 'kea'; + +import { EuiButton, EuiCallOut, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { FormattedMessage } from '@kbn/i18n-react'; + +import { docLinks } from '../../../shared/doc_links'; +import { KibanaLogic } from '../../../shared/kibana'; +import { isConnectorIndex } from '../../utils/indices'; + +import { ConvertConnectorLogic } from '../search_index/connector/native_connector_configuration/convert_connector_logic'; +import { SyncJobs } from '../search_index/sync_jobs/sync_jobs'; + +import { ConvertConnectorModal } from '../shared/convert_connector_modal/convert_connector_modal'; + +import { ConnectorStats } from './connector_stats'; +import { ConnectorViewLogic } from './connector_view_logic'; +import { OverviewLogic } from './overview.logic'; + +export const ConnectorDetailOverview: React.FC = () => { + const { indexData } = useValues(OverviewLogic); + const { connector } = useValues(ConnectorViewLogic); + const error = null; + const { isCloud } = useValues(KibanaLogic); + const { showModal } = useActions(ConvertConnectorLogic); + const { isModalVisible } = useValues(ConvertConnectorLogic); + + return ( + <> + + {isConnectorIndex(indexData) && error && ( + <> + + + {error} + + + + )} + {isConnectorIndex(indexData) && indexData.connector.is_native && !isCloud && ( + <> + {isModalVisible && } + + + +

+ + {i18n.translate( + 'xpack.enterpriseSearch.content.connectors.overview.nativeCloudCallout.connectorClient', + { defaultMessage: 'connector client' } + )} + + ), + }} + /> +

+
+ + showModal()}> + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.connectors.overview.convertConnector.buttonLabel', + { defaultMessage: 'Convert connector' } + )} + +
+ + + )} + {isConnectorIndex(indexData) && connector && ( + + )} + {isConnectorIndex(indexData) && ( + <> + + + + )} + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connectors_table.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connectors_table.tsx index 2c675c362b00e..f7ba555a3d2f7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connectors_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connectors_table.tsx @@ -27,7 +27,7 @@ import { Meta } from '../../../../../common/types/pagination'; import { generateEncodedPath } from '../../../shared/encode_path_params'; import { KibanaLogic } from '../../../shared/kibana'; import { EuiLinkTo } from '../../../shared/react_router_helpers/eui_components'; -import { SEARCH_INDEX_PATH } from '../../routes'; +import { CONNECTOR_DETAIL_PATH, SEARCH_INDEX_PATH } from '../../routes'; import { connectorStatusToColor, connectorStatusToText, @@ -59,13 +59,17 @@ export const ConnectorsTable: React.FC = ({ const { navigateToUrl } = useValues(KibanaLogic); const columns: Array> = [ { - field: 'name', name: i18n.translate( 'xpack.enterpriseSearch.content.connectors.connectorTable.columns.connectorName', { defaultMessage: 'Connector name', } ), + render: (connector: Connector) => ( + + {connector.name} + + ), width: '25%', }, { @@ -161,8 +165,8 @@ export const ConnectorsTable: React.FC = ({ ), onClick: (connector) => { navigateToUrl( - generateEncodedPath(SEARCH_INDEX_PATH, { - indexName: connector.index_name || '', + generateEncodedPath(CONNECTOR_DETAIL_PATH, { + connectorId: connector.id, }) ); }, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx index 93566c21fe999..e338b7d1f193b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx @@ -120,31 +120,10 @@ export const ConnectorConfiguration: React.FC = () => { { children: ( <> - - - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.clientExamplesLink', - { defaultMessage: 'connector client examples' } - )} - - ), - }} - /> - - { title: i18n.translate( 'xpack.enterpriseSearch.content.indices.configurationConnector.steps.deployConnector.title', { - defaultMessage: 'Deploy connector', + defaultMessage: 'Deploy connector service', } ), titleSize: 'xs', @@ -270,7 +249,7 @@ export const ConnectorConfiguration: React.FC = () => { title: i18n.translate( 'xpack.enterpriseSearch.content.indices.configurationConnector.steps.enhance.title', { - defaultMessage: 'Enhance your connector client', + defaultMessage: 'Configure your connector client', } ), titleSize: 'xs', diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/index.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/index.tsx index 53fbd691476cf..cd99649bcc88d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/index.tsx @@ -20,12 +20,14 @@ import { HttpLogic } from '../shared/http'; import { KibanaLogic } from '../shared/kibana'; import { VersionMismatchPage } from '../shared/version_mismatch'; +import { ConnectorDetailRouter } from './components/connector_detail/connector_detail_router'; import { Connectors } from './components/connectors/connectors'; import { NotFound } from './components/not_found'; import { SearchIndicesRouter } from './components/search_indices'; import { Settings } from './components/settings'; import { CONNECTORS_PATH, + CONNECTOR_DETAIL_PATH, CRAWLERS_PATH, ERROR_STATE_PATH, ROOT_PATH, @@ -77,6 +79,9 @@ export const EnterpriseSearchContentConfigured: React.FC + + + diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/routes.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/routes.ts index 9cf8628780a2a..1b9445b7fa756 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/routes.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/routes.ts @@ -34,3 +34,6 @@ export const ML_MANAGE_TRAINED_MODELS_PATH = '/app/ml/trained_models'; export const ML_NOTIFICATIONS_PATH = '/app/ml/notifications'; export const DEV_TOOLS_CONSOLE_PATH = '/app/dev_tools#/console'; + +export const CONNECTOR_DETAIL_PATH = `${CONNECTORS_PATH}/:connectorId`; +export const CONNECTOR_DETAIL_TAB_PATH = `${CONNECTOR_DETAIL_PATH}/:tabId`; diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts index 91899c88d73fc..39657c97c6202 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts @@ -545,7 +545,32 @@ export function registerConnectorRoutes({ router, log }: RouteDependencies) { }); }) ); + router.get( + { + path: '/internal/enterprise_search/connectors/{connectorId}', + validate: { + params: schema.object({ + connectorId: schema.string(), + }), + }, + }, + elasticsearchErrorHandler(log, async (context, request, response) => { + const { client } = (await context.core).elasticsearch; + const { connectorId } = request.params; + let connectorResult; + try { + connectorResult = await fetchConnectorById(client.asCurrentUser, connectorId); + } catch (error) { + throw error; + } + return response.ok({ + body: { + connector: connectorResult?.value, + }, + }); + }) + ); router.delete( { path: '/internal/enterprise_search/connectors/{connectorId}', diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx index 4352a3a6b8248..2e17153ee34e7 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx @@ -142,6 +142,13 @@ describe('Output form validation', () => { expect(res).toBeUndefined(); }); + + it('should work with hostnames using uppercase letters', () => { + const res = validateLogstashHosts(['tEsT.fr:9200', 'TEST2.fr:9200', 'teSt.fR:9999']); + + expect(res).toBeUndefined(); + }); + it('should throw for invalid hosts starting with http', () => { const res = validateLogstashHosts(['https://test.fr:5044']); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx index 330d5c5d20122..116e9f4abf157 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx @@ -166,7 +166,7 @@ export function validateLogstashHosts(value: string[]) { const url = new URL(`http://${val}`); - if (url.host !== val) { + if (url.host !== val.toLowerCase()) { throw new Error('Invalid host'); } } catch (error) { diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index fb6dd7d075cea..857882c57525f 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -31,6 +31,8 @@ import { packageServiceMock } from '../services/epm/package_service.mock'; import type { UninstallTokenServiceInterface } from '../services/security/uninstall_token_service'; import type { MessageSigningServiceInterface } from '../services/security'; +import { PackagePolicyMocks } from './package_policy.mocks'; + // Export all mocks from artifacts export * from '../services/artifacts/mocks'; @@ -40,6 +42,8 @@ export * from '../services/files/mocks'; // export all mocks from fleet actions client export * from '../services/actions/mocks'; +export * from './package_policy.mocks'; + export interface MockedFleetAppContext extends FleetAppContext { elasticsearch: ReturnType; data: ReturnType; @@ -144,6 +148,22 @@ export const createPackagePolicyServiceMock = (): jest.Mocked { + return { + async *[Symbol.asyncIterator]() { + yield Promise.resolve([PackagePolicyMocks.generatePackagePolicy({ id: '111' })]); + yield Promise.resolve([PackagePolicyMocks.generatePackagePolicy({ id: '222' })]); + }, + }; + }), + fetchAllItemIds: jest.fn((..._) => { + return { + async *[Symbol.asyncIterator]() { + yield Promise.resolve(['111']); + yield Promise.resolve(['222']); + }, + }; + }), }; }; diff --git a/x-pack/plugins/fleet/server/mocks/package_policy.mocks.ts b/x-pack/plugins/fleet/server/mocks/package_policy.mocks.ts new file mode 100644 index 0000000000000..a159917cb5e17 --- /dev/null +++ b/x-pack/plugins/fleet/server/mocks/package_policy.mocks.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SavedObjectsFindResponse } from '@kbn/core-saved-objects-api-server'; + +import type { SavedObjectsFindResult } from '@kbn/core-saved-objects-api-server'; + +import { mapPackagePolicySavedObjectToPackagePolicy } from '../services/package_policies'; + +import type { PackagePolicy } from '../../common'; +import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../common'; + +import type { PackagePolicySOAttributes } from '../types'; + +const generatePackagePolicySOAttributesMock = ( + overrides: Partial = {} +): PackagePolicySOAttributes => { + return { + name: `Package Policy 1`, + description: 'Policy for things', + created_at: '2024-01-24T15:21:13.389Z', + created_by: 'elastic', + updated_at: '2024-01-25T15:21:13.389Z', + updated_by: 'user-a', + policy_id: '444-555-666', + enabled: true, + inputs: [], + namespace: 'default', + package: { + name: 'endpoint', + title: 'Elastic Endpoint', + version: '1.0.0', + }, + revision: 1, + is_managed: false, + secret_references: [], + vars: {}, + elasticsearch: { + privileges: { + cluster: [], + }, + }, + agents: 2, + + ...overrides, + }; +}; + +const generatePackagePolicyMock = (overrides: Partial = {}) => { + return { + ...mapPackagePolicySavedObjectToPackagePolicy(generatePackagePolicySavedObjectMock()), + ...overrides, + }; +}; + +const generatePackagePolicySavedObjectMock = ( + soAttributes: PackagePolicySOAttributes = generatePackagePolicySOAttributesMock() +): SavedObjectsFindResult => { + return { + score: 1, + id: 'so-123', + type: PACKAGE_POLICY_SAVED_OBJECT_TYPE, + version: 'abc', + created_at: soAttributes.created_at, + updated_at: soAttributes.updated_at, + attributes: soAttributes, + references: [], + sort: ['created_at'], + }; +}; + +const generatePackagePolicySavedObjectFindResponseMock = ( + soResults?: PackagePolicySOAttributes[] +): SavedObjectsFindResponse => { + const soList = soResults ?? [ + generatePackagePolicySOAttributesMock(), + generatePackagePolicySOAttributesMock(), + ]; + + return { + saved_objects: soList.map((soAttributes) => { + return { + score: 1, + id: 'so-123', + type: PACKAGE_POLICY_SAVED_OBJECT_TYPE, + version: 'abc', + created_at: soAttributes.created_at, + updated_at: soAttributes.updated_at, + attributes: soAttributes, + references: [], + sort: ['created_at'], + }; + }), + total: soList.length, + per_page: 10, + page: 1, + pit_id: 'pit-id-1', + }; +}; + +export const PackagePolicyMocks = Object.freeze({ + generatePackagePolicySOAttributes: generatePackagePolicySOAttributesMock, + generatePackagePolicySavedObjectFindResponse: generatePackagePolicySavedObjectFindResponseMock, + generatePackagePolicy: generatePackagePolicyMock, +}); diff --git a/x-pack/plugins/fleet/server/services/artifacts/artifacts.test.ts b/x-pack/plugins/fleet/server/services/artifacts/artifacts.test.ts index f3b332a5930fc..782b044a84697 100644 --- a/x-pack/plugins/fleet/server/services/artifacts/artifacts.test.ts +++ b/x-pack/plugins/fleet/server/services/artifacts/artifacts.test.ts @@ -11,6 +11,8 @@ import { errors } from '@elastic/elasticsearch'; import type { TransportResult } from '@elastic/elasticsearch'; +import { set } from '@kbn/safer-lodash-set'; + import { FLEET_SERVER_ARTIFACTS_INDEX } from '../../../common'; import { ArtifactsElasticsearchError } from '../../errors'; @@ -33,12 +35,14 @@ import { createArtifact, deleteArtifact, encodeArtifactContent, + fetchAllArtifacts, generateArtifactContentHash, getArtifact, listArtifacts, } from './artifacts'; import type { NewArtifact } from './types'; +import type { FetchAllArtifactsOptions } from './types'; describe('When using the artifacts services', () => { let esClientMock: ReturnType; @@ -324,8 +328,28 @@ describe('When using the artifacts services', () => { newArtifact, ]); - expect(responseErrors).toEqual([new Error('error')]); - expect(artifacts).toBeUndefined(); + expect(responseErrors).toEqual([ + new Error( + 'Create of artifact id [undefined] returned: result [undefined], status [400], reason [{"reason":"error"}]' + ), + ]); + expect(artifacts).toEqual([ + { + body: 'eJyrVkrNKynKTC1WsoqOrQUAJxkFKQ==', + compressionAlgorithm: 'zlib', + created: expect.any(String), + decodedSha256: 'd801aa1fb', + decodedSize: 14, + encodedSha256: 'd29238d40', + encodedSize: 22, + encryptionAlgorithm: 'none', + id: 'endpoint:trustlist-v1-d801aa1fb', + identifier: 'trustlist-v1', + packageName: 'endpoint', + relative_url: '/api/fleet/artifacts/trustlist-v1/d801aa1fb', + type: 'trustlist', + }, + ]); }); }); @@ -488,4 +512,113 @@ describe('When using the artifacts services', () => { }); }); }); + + describe('and calling `fetchAll()`', () => { + beforeEach(() => { + esClientMock.search + .mockResolvedValueOnce(generateArtifactEsSearchResultHitsMock()) + .mockResolvedValueOnce(generateArtifactEsSearchResultHitsMock()) + .mockResolvedValueOnce(set(generateArtifactEsSearchResultHitsMock(), 'hits.hits', [])); + }); + + it('should return an iterator', async () => { + expect(fetchAllArtifacts(esClientMock)).toEqual({ + [Symbol.asyncIterator]: expect.any(Function), + }); + }); + + it('should provide artifacts on each iteration', async () => { + for await (const artifacts of fetchAllArtifacts(esClientMock)) { + expect(artifacts[0]).toEqual({ + body: expect.anything(), + compressionAlgorithm: expect.anything(), + created: expect.anything(), + decodedSha256: expect.anything(), + decodedSize: expect.anything(), + encodedSha256: expect.anything(), + encodedSize: expect.anything(), + encryptionAlgorithm: expect.anything(), + id: expect.anything(), + identifier: expect.anything(), + packageName: expect.anything(), + relative_url: expect.anything(), + type: expect.anything(), + }); + } + + expect(esClientMock.search).toHaveBeenCalledTimes(3); + }); + + it('should use defaults if no `options` were provided', async () => { + for await (const artifacts of fetchAllArtifacts(esClientMock)) { + expect(artifacts.length).toBeGreaterThan(0); + } + + expect(esClientMock.search).toHaveBeenLastCalledWith( + expect.objectContaining({ + q: '', + size: 1000, + sort: [{ created: { order: 'asc' } }], + _source_excludes: undefined, + }) + ); + }); + + it('should use custom options when provided', async () => { + const options: FetchAllArtifactsOptions = { + kuery: 'foo: something', + sortOrder: 'desc', + perPage: 500, + sortField: 'someField', + includeArtifactBody: false, + }; + + for await (const artifacts of fetchAllArtifacts(esClientMock, options)) { + expect(artifacts.length).toBeGreaterThan(0); + } + + expect(esClientMock.search).toHaveBeenCalledWith( + expect.objectContaining({ + q: options.kuery, + size: options.perPage, + sort: [{ [options.sortField!]: { order: options.sortOrder } }], + _source_excludes: 'body', + }) + ); + }); + + it('should set `done` to true if loop `break`s out', async () => { + const iterator = fetchAllArtifacts(esClientMock); + + for await (const _ of iterator) { + break; + } + + await expect(iterator[Symbol.asyncIterator]().next()).resolves.toEqual({ + done: true, + value: expect.any(Array), + }); + + expect(esClientMock.search).toHaveBeenCalledTimes(1); + }); + + it('should handle throwing in loop by setting `done` to `true`', async () => { + const iterator = fetchAllArtifacts(esClientMock); + + try { + for await (const _ of iterator) { + throw new Error('test'); + } + } catch (e) { + expect(e); // just to silence eslint + } + + await expect(iterator[Symbol.asyncIterator]().next()).resolves.toEqual({ + done: true, + value: expect.any(Array), + }); + + expect(esClientMock.search).toHaveBeenCalledTimes(1); + }); + }); }); diff --git a/x-pack/plugins/fleet/server/services/artifacts/artifacts.ts b/x-pack/plugins/fleet/server/services/artifacts/artifacts.ts index 5516ab6f70e23..43cf3f745cc6c 100644 --- a/x-pack/plugins/fleet/server/services/artifacts/artifacts.ts +++ b/x-pack/plugins/fleet/server/services/artifacts/artifacts.ts @@ -15,6 +15,8 @@ import { isEmpty, sortBy } from 'lodash'; import type { ElasticsearchClient } from '@kbn/core/server'; +import { createEsSearchIterable } from '../utils/create_es_search_iterable'; + import type { ListResult } from '../../../common/types'; import { FLEET_SERVER_ARTIFACTS_INDEX } from '../../../common'; @@ -34,6 +36,7 @@ import type { ArtifactsClientCreateOptions, ListArtifactsProps, NewArtifact, + FetchAllArtifactsOptions, } from './types'; import { esSearchHitToArtifact, @@ -137,10 +140,10 @@ export const bulkCreateArtifacts = async ( artifacts, appContextService.getConfig()?.createArtifactsBulkBatchSize ); - const logger = appContextService.getLogger(); const nonConflictErrors = []; logger.debug(`Number of batches generated for fleet artifacts: ${batches.length}`); + for (let batchN = 0; batchN < batches.length; batchN++) { logger.debug( `Creating artifacts for batch ${batchN + 1} with ${batches[batchN].length / 2} artifacts` @@ -154,12 +157,22 @@ export const bulkCreateArtifacts = async ( refresh, }) ); + // Track errors of the bulk create action if (res.errors) { nonConflictErrors.push( ...res.items.reduce((acc, item) => { - if (item.create?.status !== 409) { - acc.push(new Error(item.create?.error?.reason)); + // 409's (conflict - record already exists) are ignored since the artifact already exists + if (item.create && item.create.status !== 409) { + acc.push( + new Error( + `Create of artifact id [${item.create._id}] returned: result [${ + item.create.result + }], status [${item.create.status}], reason [${JSON.stringify( + item.create?.error || '' + )}]` + ) + ); } return acc; }, []) @@ -167,11 +180,6 @@ export const bulkCreateArtifacts = async ( } } - // If any non conflict error, it returns only the errors - if (nonConflictErrors.length > 0) { - return { errors: nonConflictErrors }; - } - // Use non sorted artifacts array to preserve the artifacts order in the response const nonSortedEsArtifactsResponse: Artifact[] = artifacts.map((artifact) => { return esSearchHitToArtifact({ @@ -182,6 +190,7 @@ export const bulkCreateArtifacts = async ( return { artifacts: nonSortedEsArtifactsResponse, + errors: nonConflictErrors.length ? nonConflictErrors : undefined, }; }; @@ -281,3 +290,66 @@ export const encodeArtifactContent = async ( return encodedArtifact; }; + +/** + * Returns an iterator that loops through all the artifacts stored in the index + * + * @param esClient + * @param options + * + * @example + * + * async () => { + * for await (const value of fetchAllArtifactsIterator()) { + * // process page of data here + * } + * } + */ +export const fetchAllArtifacts = ( + esClient: ElasticsearchClient, + options: FetchAllArtifactsOptions = {} +): AsyncIterable => { + const { kuery = '', perPage = 1000, sortField, sortOrder, includeArtifactBody = true } = options; + + return createEsSearchIterable({ + esClient, + searchRequest: { + index: FLEET_SERVER_ARTIFACTS_INDEX, + rest_total_hits_as_int: true, + track_total_hits: false, + q: kuery, + size: perPage, + sort: [ + { + // MUST have a sort field and sort order + [sortField || 'created']: { + order: sortOrder || 'asc', + }, + }, + ], + _source_excludes: includeArtifactBody ? undefined : 'body', + }, + resultsMapper: (data): Artifact[] => { + return data.hits.hits.map((hit) => { + // @ts-expect-error @elastic/elasticsearch _source is optional + const artifact = esSearchHitToArtifact(hit); + + // If not body attribute is included, still create the property in the object (since the + // return type is `Artifact` and `body` is required), but throw an error is caller attempts + // to still access it. + if (!includeArtifactBody) { + Object.defineProperty(artifact, 'body', { + enumerable: false, + get(): string { + throw new Error( + `'body' attribute not included due to request to 'fetchAllArtifacts()' having options 'includeArtifactBody' set to 'false'` + ); + }, + }); + } + + return artifact; + }); + }, + }); +}; diff --git a/x-pack/plugins/fleet/server/services/artifacts/client.ts b/x-pack/plugins/fleet/server/services/artifacts/client.ts index 7ba2452e83fe7..0b40a7acdcc8d 100644 --- a/x-pack/plugins/fleet/server/services/artifacts/client.ts +++ b/x-pack/plugins/fleet/server/services/artifacts/client.ts @@ -17,6 +17,7 @@ import type { ArtifactsClientInterface, NewArtifact, ListArtifactsProps, + FetchAllArtifactsOptions, } from './types'; import { relativeDownloadUrlFromArtifact, uniqueIdFromId } from './mappings'; @@ -29,6 +30,7 @@ import { listArtifacts, bulkCreateArtifacts, bulkDeleteArtifacts, + fetchAllArtifacts, } from './artifacts'; /** @@ -49,6 +51,15 @@ export class FleetArtifactsClient implements ArtifactsClientInterface { return artifact; } + /** + * Creates a `kuery` string using the provided value on input that is bound to the integration package + * @param kuery + * @private + */ + private buildFilter(kuery: string): string { + return `(package_name: "${this.packageName}")${kuery ? ` AND ${kuery}` : ''}`; + } + async getArtifact(id: string): Promise { const artifact = await getArtifact(this.esClient, id); return artifact ? this.validate(artifact) : undefined; @@ -119,20 +130,37 @@ export class FleetArtifactsClient implements ArtifactsClientInterface { } /** - * Get a list of artifacts. - * NOTE that when using the `kuery` filtering param, that all filters property names should - * match the internal attribute names of the index + * Get a list of artifacts. A few things to note: + * - if wanting to get ALL artifacts, consider using instead the `fetchAll()` method instead + * as it will property return data past the 10k ES limitation + * - when using the `kuery` filtering param, all filters property names should match the + * internal attribute names in the index */ async listArtifacts({ kuery, ...options }: ListArtifactsProps = {}): Promise< ListResult > { - // All filtering for artifacts should be bound to the `packageName`, so we insert - // that into the KQL value and use `AND` to add the defined `kuery` (if any) to it. - const filter = `(package_name: "${this.packageName}")${kuery ? ` AND ${kuery}` : ''}`; - return listArtifacts(this.esClient, { ...options, - kuery: filter, + kuery: this.buildFilter(kuery), + }); + } + + /** + * Returns an `AsyncIterable` object that can be used to iterate over all artifacts + * + * @param options + * + * @example + * async () => { + * for await (const artifacts of fleetArtifactsClient.fetchAll()) { + * // artifacts === first page of items + * } + * } + */ + fetchAll({ kuery, ...options }: FetchAllArtifactsOptions = {}): AsyncIterable { + return fetchAllArtifacts(this.esClient, { + ...options, + kuery: this.buildFilter(kuery), }); } diff --git a/x-pack/plugins/fleet/server/services/artifacts/mocks.ts b/x-pack/plugins/fleet/server/services/artifacts/mocks.ts index dc831558cb7bb..4e5d8c93f0643 100644 --- a/x-pack/plugins/fleet/server/services/artifacts/mocks.ts +++ b/x-pack/plugins/fleet/server/services/artifacts/mocks.ts @@ -44,6 +44,34 @@ export const createArtifactsClientMock = (): jest.Mocked { + return createFetchAllArtifactsIterableMock(); + }), + }; +}; + +export const createFetchAllArtifactsIterableMock = (artifactPages: Artifact[][] = []) => { + const totalPagesOfResults = artifactPages.length; + let nextResults = 0; + + return { + [Symbol.asyncIterator]() { + return { + async next() { + return { + value: artifactPages[nextResults++] ?? [], + done: nextResults > totalPagesOfResults, + }; + }, + + async return() { + return { + value: [], + done: true, + }; + }, + }; + }, }; }; @@ -100,6 +128,7 @@ export const generateArtifactEsGetSingleHitMock = ( _version: 1, _score: 1, _source, + sort: ['abc'], }; }; diff --git a/x-pack/plugins/fleet/server/services/artifacts/types.ts b/x-pack/plugins/fleet/server/services/artifacts/types.ts index 4b0aacd92bc20..697815a593fdd 100644 --- a/x-pack/plugins/fleet/server/services/artifacts/types.ts +++ b/x-pack/plugins/fleet/server/services/artifacts/types.ts @@ -72,6 +72,12 @@ export type ListArtifactsProps = Pick & { + sortField?: string | keyof ArtifactElasticsearchProperties; + /** If false, then the `body` property of the Artifact will be excluded from the results. Default is `true` */ + includeArtifactBody?: boolean; +}; + /** * The interface exposed out of Fleet's Artifact service via the client class */ @@ -93,4 +99,6 @@ export interface ArtifactsClientInterface { encodeContent(content: ArtifactsClientCreateOptions['content']): Promise; generateHash(content: string): string; + + fetchAll(options?: FetchAllArtifactsOptions): AsyncIterable; } 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 97402447be716..9dfd307725945 100644 --- a/x-pack/plugins/fleet/server/services/epm/registry/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/registry/index.ts @@ -94,16 +94,8 @@ async function _fetchFindLatestPackage( const bundledPackage = await getBundledPackageByName(packageName); - // temporary workaround to allow synthetics package beta version until there is a GA available - // needed because synthetics is installed by default on kibana startup - const prereleaseAllowedExceptions = ['synthetics']; - - const prereleaseEnabled = prerelease || prereleaseAllowedExceptions.includes(packageName); - const registryUrl = getRegistryUrl(); - const url = new URL( - `${registryUrl}/search?package=${packageName}&prerelease=${prereleaseEnabled}` - ); + const url = new URL(`${registryUrl}/search?package=${packageName}&prerelease=${prerelease}`); if (!ignoreConstraints) { setConstraints(url); diff --git a/x-pack/plugins/fleet/server/services/package_policies/index.ts b/x-pack/plugins/fleet/server/services/package_policies/index.ts index d0d4fa4aae825..a7eacdc76a3a7 100644 --- a/x-pack/plugins/fleet/server/services/package_policies/index.ts +++ b/x-pack/plugins/fleet/server/services/package_policies/index.ts @@ -7,3 +7,4 @@ export * from './experimental_datastream_features'; export * from './package_policy_name_helper'; +export * from './utils'; diff --git a/x-pack/plugins/fleet/server/services/package_policies/utils.test.ts b/x-pack/plugins/fleet/server/services/package_policies/utils.test.ts new file mode 100644 index 0000000000000..363ffe9c38fa4 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/package_policies/utils.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PackagePolicyMocks } from '../../mocks'; + +import { mapPackagePolicySavedObjectToPackagePolicy } from './utils'; + +describe('Package Policy Utils', () => { + describe('mapPackagePolicySavedObjectToPackagePolicy()', () => { + it('should return only exposed SO properties', () => { + const soItem = + PackagePolicyMocks.generatePackagePolicySavedObjectFindResponse().saved_objects.at(0)!; + + expect(mapPackagePolicySavedObjectToPackagePolicy(soItem)).toEqual({ + agents: 2, + created_at: '2024-01-24T15:21:13.389Z', + created_by: 'elastic', + description: 'Policy for things', + elasticsearch: { + privileges: { + cluster: [], + }, + }, + enabled: true, + id: 'so-123', + inputs: [], + is_managed: false, + name: 'Package Policy 1', + namespace: 'default', + package: { + name: 'endpoint', + title: 'Elastic Endpoint', + version: '1.0.0', + }, + policy_id: '444-555-666', + revision: 1, + secret_references: [], + updated_at: '2024-01-25T15:21:13.389Z', + updated_by: 'user-a', + vars: {}, + version: 'abc', + }); + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/package_policies/utils.ts b/x-pack/plugins/fleet/server/services/package_policies/utils.ts new file mode 100644 index 0000000000000..309db211bbf14 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/package_policies/utils.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SavedObject } from '@kbn/core-saved-objects-common/src/server_types'; + +import type { PackagePolicy, PackagePolicySOAttributes } from '../../types'; + +export const mapPackagePolicySavedObjectToPackagePolicy = ({ + /* eslint-disable @typescript-eslint/naming-convention */ + id, + version, + attributes: { + name, + description, + namespace, + enabled, + is_managed, + policy_id, + // `package` is a reserved keyword + package: packageInfo, + inputs, + vars, + elasticsearch, + agents, + revision, + secret_references, + updated_at, + updated_by, + created_at, + created_by, + /* eslint-enable @typescript-eslint/naming-convention */ + }, +}: SavedObject): PackagePolicy => { + return { + id, + name, + description, + namespace, + enabled, + is_managed, + policy_id, + package: packageInfo, + inputs, + vars, + elasticsearch, + version, + agents, + revision, + secret_references, + updated_at, + updated_by, + created_at, + created_by, + }; +}; diff --git a/x-pack/plugins/fleet/server/services/package_policy.test.ts b/x-pack/plugins/fleet/server/services/package_policy.test.ts index 24483be93a9f5..cc605900c3a58 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.test.ts @@ -19,6 +19,8 @@ import type { } from '@kbn/core/server'; import { SavedObjectsErrorHelpers } from '@kbn/core/server'; +import { PackagePolicyMocks } from '../mocks/package_policy.mocks'; + import type { PackageInfo, PackagePolicySOAttributes, @@ -53,6 +55,8 @@ import { import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../constants'; +import { mapPackagePolicySavedObjectToPackagePolicy } from './package_policies'; + import { preconfigurePackageInputs, updatePackageInputs, @@ -4918,6 +4922,149 @@ describe('Package policy service', () => { ).rejects.toEqual(new FleetError('Package notinstalled is not installed')); }); }); + + describe('fetchAllItemIds()', () => { + let soClientMock: ReturnType; + + beforeEach(() => { + soClientMock = savedObjectsClientMock.create(); + + soClientMock.find + .mockResolvedValueOnce(PackagePolicyMocks.generatePackagePolicySavedObjectFindResponse()) + .mockResolvedValueOnce(PackagePolicyMocks.generatePackagePolicySavedObjectFindResponse()) + .mockResolvedValueOnce( + Object.assign(PackagePolicyMocks.generatePackagePolicySavedObjectFindResponse(), { + saved_objects: [], + }) + ); + }); + + it('should return an iterator', async () => { + expect(packagePolicyService.fetchAllItemIds(soClientMock)).toEqual({ + [Symbol.asyncIterator]: expect.any(Function), + }); + }); + + it('should provide item ids on every iteration', async () => { + for await (const ids of packagePolicyService.fetchAllItemIds(soClientMock)) { + expect(ids).toEqual(['so-123', 'so-123']); + } + + expect(soClientMock.find).toHaveBeenCalledTimes(3); + }); + + it('should use default options', async () => { + for await (const ids of packagePolicyService.fetchAllItemIds(soClientMock)) { + expect(ids); + } + + expect(soClientMock.find).toHaveBeenCalledWith( + expect.objectContaining({ + type: PACKAGE_POLICY_SAVED_OBJECT_TYPE, + perPage: 1000, + sortField: 'created_at', + sortOrder: 'asc', + fields: [], + filter: undefined, + }) + ); + }); + + it('should use custom options when defined', async () => { + for await (const ids of packagePolicyService.fetchAllItemIds(soClientMock, { + perPage: 13, + kuery: 'one=two', + })) { + expect(ids); + } + + expect(soClientMock.find).toHaveBeenCalledWith( + expect.objectContaining({ + type: PACKAGE_POLICY_SAVED_OBJECT_TYPE, + perPage: 13, + sortField: 'created_at', + sortOrder: 'asc', + fields: [], + filter: 'one=two', + }) + ); + }); + }); + + describe('fetchAllItems()', () => { + let soClientMock: ReturnType; + + beforeEach(() => { + soClientMock = savedObjectsClientMock.create(); + + soClientMock.find + .mockResolvedValueOnce(PackagePolicyMocks.generatePackagePolicySavedObjectFindResponse()) + .mockResolvedValueOnce(PackagePolicyMocks.generatePackagePolicySavedObjectFindResponse()) + .mockResolvedValueOnce( + Object.assign(PackagePolicyMocks.generatePackagePolicySavedObjectFindResponse(), { + saved_objects: [], + }) + ); + }); + + it('should return an iterator', async () => { + expect(packagePolicyService.fetchAllItems(soClientMock)).toEqual({ + [Symbol.asyncIterator]: expect.any(Function), + }); + }); + + it('should provide items on every iteration', async () => { + for await (const items of packagePolicyService.fetchAllItems(soClientMock)) { + expect(items).toEqual( + PackagePolicyMocks.generatePackagePolicySavedObjectFindResponse().saved_objects.map( + (soItem) => { + return mapPackagePolicySavedObjectToPackagePolicy(soItem); + } + ) + ); + } + + expect(soClientMock.find).toHaveBeenCalledTimes(3); + }); + + it('should use default options', async () => { + for await (const ids of packagePolicyService.fetchAllItemIds(soClientMock)) { + expect(ids); + } + + expect(soClientMock.find).toHaveBeenCalledWith( + expect.objectContaining({ + type: PACKAGE_POLICY_SAVED_OBJECT_TYPE, + perPage: 1000, + sortField: 'created_at', + sortOrder: 'asc', + fields: [], + filter: undefined, + }) + ); + }); + + it('should use custom options when defined', async () => { + for await (const ids of packagePolicyService.fetchAllItems(soClientMock, { + kuery: 'one=two', + perPage: 12, + sortOrder: 'desc', + sortField: 'updated_by', + })) { + expect(ids); + } + + expect(soClientMock.find).toHaveBeenCalledWith( + expect.objectContaining({ + type: PACKAGE_POLICY_SAVED_OBJECT_TYPE, + perPage: 12, + sortField: 'updated_by', + sortOrder: 'desc', + filter: 'one=two', + }) + ); + }); + }); }); describe('getUpgradeDryRunDiff', () => { diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index e89ac0160f62c..45753540af256 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -95,6 +95,8 @@ import type { } from '../types'; import type { ExternalCallback } from '..'; +import { createSoFindIterable } from './utils/create_so_find_iterable'; + import type { FleetAuthzRouteConfig } from './security'; import { getAuthzFromRequest, doesNotHaveRequiredFleetAuthz } from './security'; @@ -109,9 +111,16 @@ import { appContextService } from '.'; import { removeOldAssets } from './epm/packages/cleanup'; import type { PackageUpdateEvent, UpdateEventType } from './upgrade_sender'; import { sendTelemetryEvents } from './upgrade_sender'; -import { handleExperimentalDatastreamFeatureOptIn } from './package_policies'; +import { + handleExperimentalDatastreamFeatureOptIn, + mapPackagePolicySavedObjectToPackagePolicy, +} from './package_policies'; import { updateDatastreamExperimentalFeatures } from './epm/packages/update'; -import type { PackagePolicyClient, PackagePolicyService } from './package_policy_service'; +import type { + PackagePolicyClient, + PackagePolicyClientFetchAllItemsOptions, + PackagePolicyService, +} from './package_policy_service'; import { installAssetsForInputPackagePolicy } from './epm/packages/install'; import { auditLoggingService } from './audit_logging'; import { @@ -122,6 +131,7 @@ import { } from './secrets'; import { getPackageAssetsMap } from './epm/packages/get'; import { validateOutputForNewPackagePolicy } from './agent_policies/outputs_helpers'; +import type { PackagePolicyClientFetchAllItemIdsOptions } from './package_policy_service'; export type InputsOverride = Partial & { vars?: Array; @@ -1886,6 +1896,60 @@ class PackagePolicyClientImpl implements PackagePolicyClient { } } } + + fetchAllItemIds( + soClient: SavedObjectsClientContract, + { perPage = 1000, kuery }: PackagePolicyClientFetchAllItemIdsOptions = {} + ): AsyncIterable { + // TODO:PT Question for fleet team: do I need to `auditLoggingService.writeCustomSoAuditLog()` here? Its only IDs + + return createSoFindIterable<{}>({ + soClient, + findRequest: { + type: SAVED_OBJECT_TYPE, + perPage, + sortField: 'created_at', + sortOrder: 'asc', + fields: [], + filter: kuery ? normalizeKuery(SAVED_OBJECT_TYPE, kuery) : undefined, + }, + resultsMapper: (data) => { + return data.saved_objects.map((packagePolicySO) => packagePolicySO.id); + }, + }); + } + + fetchAllItems( + soClient: SavedObjectsClientContract, + { + perPage = 1000, + kuery, + sortOrder = 'asc', + sortField = 'created_at', + }: PackagePolicyClientFetchAllItemsOptions = {} + ): AsyncIterable { + return createSoFindIterable({ + soClient, + findRequest: { + type: SAVED_OBJECT_TYPE, + sortField, + sortOrder, + perPage, + filter: kuery ? normalizeKuery(SAVED_OBJECT_TYPE, kuery) : undefined, + }, + resultsMapper(data) { + return data.saved_objects.map((packagePolicySO) => { + auditLoggingService.writeCustomSoAuditLog({ + action: 'find', + id: packagePolicySO.id, + savedObjectType: PACKAGE_POLICY_SAVED_OBJECT_TYPE, + }); + + return mapPackagePolicySavedObjectToPackagePolicy(packagePolicySO); + }); + }, + }); + } } export class PackagePolicyServiceImpl 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 9519cafbc6a73..de960c44b7879 100644 --- a/x-pack/plugins/fleet/server/services/package_policy_service.ts +++ b/x-pack/plugins/fleet/server/services/package_policy_service.ts @@ -213,4 +213,31 @@ export interface PackagePolicyClient { packageInfo: PackageInfo; experimentalDataStreamFeatures: ExperimentalDataStreamFeature[]; }>; + + /** + * Returns an `AsyncIterable` for retrieving all integration policy IDs + * @param soClient + * @param options + */ + fetchAllItemIds( + soClient: SavedObjectsClientContract, + options?: PackagePolicyClientFetchAllItemIdsOptions + ): AsyncIterable; + + /** + * Returns an `AsyncIterable` for retrieving all integration policies + * @param soClient + * @param options + */ + fetchAllItems( + soClient: SavedObjectsClientContract, + options?: PackagePolicyClientFetchAllItemsOptions + ): AsyncIterable; } + +export type PackagePolicyClientFetchAllItemIdsOptions = Pick; + +export type PackagePolicyClientFetchAllItemsOptions = Pick< + ListWithKuery, + 'perPage' | 'kuery' | 'sortField' | 'sortOrder' +>; diff --git a/x-pack/plugins/fleet/server/services/utils/create_es_search_iterable.ts b/x-pack/plugins/fleet/server/services/utils/create_es_search_iterable.ts new file mode 100644 index 0000000000000..ae4cb9551bc8c --- /dev/null +++ b/x-pack/plugins/fleet/server/services/utils/create_es_search_iterable.ts @@ -0,0 +1,165 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; + +import type * as estypes from '@kbn/es-types'; + +import type { SearchRequest, SearchResponse } from '@elastic/elasticsearch/lib/api/types'; + +export interface CreateEsSearchIterableOptions { + esClient: ElasticsearchClient; + searchRequest: Omit & + Pick, 'sort' | 'index'>; + /** + * An optional callback for mapping the results retrieved from ES. If defined, the iterator + * `value` will be set to the data returned by this mapping function. + * + * @param data + */ + resultsMapper?: (data: SearchResponse) => any; + /** If a Point in Time should be used while executing the search. Defaults to `true` */ + usePointInTime?: boolean; +} + +export type InferEsSearchIteratorResultValue = + CreateEsSearchIterableOptions['resultsMapper'] extends undefined + ? SearchResponse + : ReturnType>['resultsMapper']>; + +/** + * Creates an `AsyncIterable` that can be used to iterate (ex. via `for..await..of`) over all the data + * matching the search query. The search request to ES will use `search_after`, thus can iterate over + * datasets above 10k items as well. + * + * @param options + * + * @example + * + * const yourFn = async () => { + * const dataIterable = createEsSearchIterable({ + * esClient, + * searchRequest: { + * index: 'some-index', + * sort: [ + * { + * created: { order: 'asc' } + * } + * ] + * } + * }); + * + * for await (const data of dataIterable) { + * // data === your search results + * } + * } + */ +export const createEsSearchIterable = ({ + esClient, + searchRequest: { size = 1000, index, ...searchOptions }, + resultsMapper, + usePointInTime = true, +}: CreateEsSearchIterableOptions): AsyncIterable< + InferEsSearchIteratorResultValue +> => { + const keepAliveValue = '5m'; + let done = false; + let value: SearchResponse; + let searchAfterValue: estypes.SearchHit['sort'] | undefined; + let pointInTime: Promise<{ id: string }> = usePointInTime + ? esClient.openPointInTime({ + index, + ignore_unavailable: true, + keep_alive: keepAliveValue, + }) + : Promise.resolve({ id: '' }); + + const createIteratorResult = (): IteratorResult> => { + return { done, value }; + }; + + const setValue = (searchResponse: SearchResponse): void => { + value = resultsMapper ? resultsMapper(searchResponse) : searchResponse; + }; + + const setDone = async (): Promise => { + done = true; + + if (usePointInTime) { + const pitId = (await pointInTime).id; + + if (pitId) { + await esClient.closePointInTime({ id: pitId }); + } + } + }; + + const fetchData = async () => { + const pitId = (await pointInTime).id; + + const searchResult = await esClient + .search({ + ...searchOptions, + size, + ...(usePointInTime + ? { + pit: { + id: pitId, + keep_alive: keepAliveValue, + }, + } + : { index }), + search_after: searchAfterValue, + }) + .catch((e) => { + Error.captureStackTrace(e); + throw e; + }); + + const searchHits = searchResult.hits.hits; + const lastSearchHit = searchHits[searchHits.length - 1]; + + if (searchHits.length === 0) { + await setDone(); + return; + } + + searchAfterValue = lastSearchHit.sort; + pointInTime = Promise.resolve({ id: searchResult.pit_id ?? '' }); + setValue(searchResult); + + // If (for some reason) we don't have a `searchAfterValue`, + // then throw an error, or else we'll keep looping forever + if (!searchAfterValue) { + await setDone(); + throw new Error( + `Unable to store 'search_after' value. Last 'SearchHit' did not include a 'sort' property \n(did you forget to set the 'sort' attribute on your SearchRequest?)':\n${JSON.stringify( + lastSearchHit + )}` + ); + } + }; + + return { + [Symbol.asyncIterator]() { + return { + async next() { + if (!done) { + await fetchData(); + } + + return createIteratorResult(); + }, + + async return() { + done = true; + return createIteratorResult(); + }, + }; + }, + }; +}; diff --git a/x-pack/plugins/fleet/server/services/utils/create_so_find_iterable.ts b/x-pack/plugins/fleet/server/services/utils/create_so_find_iterable.ts new file mode 100644 index 0000000000000..6b17b3ba98040 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/utils/create_so_find_iterable.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 { + SavedObjectsClientContract, + SavedObjectsFindOptions, + SavedObjectsFindResponse, + SavedObjectsFindResult, +} from '@kbn/core-saved-objects-api-server'; + +export interface CreateSoFindIterableOptions { + soClient: SavedObjectsClientContract; + findRequest: Omit & + // sortField is required + Pick, 'sortField'>; + /** + * An optional callback for mapping the results retrieved from SavedObjects. If defined, the iterator + * `value` will be set to the data returned by this mapping function. + * + * @param data + */ + resultsMapper?: (data: SavedObjectsFindResponse) => any; + /** If a Point in Time should be used while executing the search. Defaults to `true` */ + usePointInTime?: boolean; +} + +export type InferSoFindIteratorResultValue = + CreateSoFindIterableOptions['resultsMapper'] extends undefined + ? SavedObjectsFindResponse + : ReturnType>['resultsMapper']>; + +/** + * Creates an `AsyncIterable` that can be used to iterate (ex. via `for..await..of`) over all the data + * matching the search query. The search request to Saved Object will use `searchAfter`, thus can iterate over + * datasets above 10k items as well. + * + * @param options + */ +export const createSoFindIterable = ({ + soClient, + findRequest: { perPage = 1000, ...findOptions }, + resultsMapper, + usePointInTime = true, +}: CreateSoFindIterableOptions): AsyncIterable< + InferSoFindIteratorResultValue +> => { + const keepAliveValue = '5m'; + let done = false; + let value: SavedObjectsFindResponse; + let searchAfterValue: SavedObjectsFindResult['sort'] | undefined; + let pointInTime: Promise<{ id: string }> = usePointInTime + ? soClient.openPointInTimeForType(findOptions.type, { keepAlive: keepAliveValue }) + : Promise.resolve({ id: '' }); + + const setValue = (findResponse: SavedObjectsFindResponse): void => { + value = resultsMapper ? resultsMapper(findResponse) : findResponse; + }; + + const setDone = async (): Promise => { + done = true; + + if (usePointInTime) { + const pitId = (await pointInTime).id; + + if (pitId) { + await soClient.closePointInTime(pitId); + } + } + }; + + const fetchData = async () => { + const findResult = await soClient + .find({ + ...findOptions, + ...(usePointInTime + ? { + pit: { + id: (await pointInTime).id, + keepAlive: keepAliveValue, + }, + } + : {}), + perPage, + searchAfter: searchAfterValue, + }) + .catch((e) => { + Error.captureStackTrace(e); + throw e; + }); + + const soItems = findResult.saved_objects; + const lastSearchHit = soItems[soItems.length - 1]; + + if (soItems.length === 0) { + setValue(findResult); + await setDone(); + return; + } + + searchAfterValue = lastSearchHit.sort; + pointInTime = Promise.resolve({ id: findResult.pit_id ?? '' }); + setValue(findResult); + + // If (for some reason) we don't have a `searchAfterValue`, + // then throw an error, or else we'll keep looping forever + if (!searchAfterValue) { + await setDone(); + throw new Error( + `Unable to store 'searchAfter' value. Last 'SavedObjectsFindResult' did not include a 'sort' property \n(did you forget to set the 'sortField' attribute on your SavedObjectsFindOptions?)':\n${JSON.stringify( + lastSearchHit + )}` + ); + } + }; + + const createIteratorResult = (): IteratorResult> => { + return { done, value }; + }; + + return { + [Symbol.asyncIterator]() { + return { + async next() { + if (!done) { + await fetchData(); + } + + return createIteratorResult(); + }, + + async return() { + done = true; + return createIteratorResult(); + }, + }; + }, + }; +}; diff --git a/x-pack/plugins/fleet/server/types/models/output.test.ts b/x-pack/plugins/fleet/server/types/models/output.test.ts index 06edd900fec2a..9c850766b9ba8 100644 --- a/x-pack/plugins/fleet/server/types/models/output.test.ts +++ b/x-pack/plugins/fleet/server/types/models/output.test.ts @@ -13,6 +13,10 @@ describe('Output model', () => { expect(validateLogstashHost('test.fr:5044')).toBeUndefined(); }); + it('should support valid host with uppercase letters', () => { + expect(validateLogstashHost('tEsT.fr:5044')).toBeUndefined(); + }); + it('should return an error for an invalid host', () => { expect(validateLogstashHost('!@#%&!#!@')).toMatchInlineSnapshot(`"Invalid Logstash host"`); }); diff --git a/x-pack/plugins/fleet/server/types/models/output.ts b/x-pack/plugins/fleet/server/types/models/output.ts index 730ec512f5a0f..765018f3ac88f 100644 --- a/x-pack/plugins/fleet/server/types/models/output.ts +++ b/x-pack/plugins/fleet/server/types/models/output.ts @@ -26,7 +26,7 @@ export function validateLogstashHost(val: string) { try { const url = new URL(`http://${val}`); - if (url.host !== val) { + if (url.host !== val.toLowerCase()) { return 'Invalid host'; } } catch (err) { diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/helpers.ts b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/helpers.ts index 4de8e8675b433..475862664c336 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/helpers.ts +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/helpers.ts @@ -4,13 +4,12 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { i18n } from '@kbn/i18n'; import { getIndexPatternFromSQLQuery, getIndexPatternFromESQLQuery } from '@kbn/esql-utils'; -import type { AggregateQuery, Query, Filter } from '@kbn/es-query'; +import type { AggregateQuery } from '@kbn/es-query'; import { getESQLAdHocDataview } from '@kbn/esql-utils'; +import { getLensAttributesFromSuggestion } from '@kbn/visualization-utils'; import { fetchFieldsFromESQL } from '@kbn/text-based-editor'; -import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/public'; -import type { Suggestion } from '../../../types'; +import type { DataViewSpec } from '@kbn/data-views-plugin/public'; import type { TypedLensByValueInput } from '../../../embeddable/embeddable_component'; import type { LensPluginStartDependencies } from '../../../plugin'; import type { DatasourceMap, VisualizationMap } from '../../../types'; @@ -72,68 +71,15 @@ export const getSuggestions = async ( const firstSuggestion = allSuggestions[0]; - const attrs = getLensAttributes({ + const attrs = getLensAttributesFromSuggestion({ filters: [], query, suggestion: firstSuggestion, dataView, - }); + }) as TypedLensByValueInput['attributes']; return attrs; } catch (e) { setErrors([e]); } return undefined; }; - -export const getLensAttributes = ({ - filters, - query, - suggestion, - dataView, -}: { - filters: Filter[]; - query: Query | AggregateQuery; - suggestion: Suggestion | undefined; - dataView?: DataView; -}) => { - const suggestionDatasourceState = Object.assign({}, suggestion?.datasourceState); - const suggestionVisualizationState = Object.assign({}, suggestion?.visualizationState); - const datasourceStates = - suggestion && suggestion.datasourceState - ? { - [suggestion.datasourceId!]: { - ...suggestionDatasourceState, - }, - } - : { - formBased: {}, - }; - const visualization = suggestionVisualizationState; - const attributes = { - title: suggestion - ? suggestion.title - : i18n.translate('xpack.lens.config.suggestion.title', { - defaultMessage: 'New suggestion', - }), - references: [ - { - id: dataView?.id ?? '', - name: `textBasedLanguages-datasource-layer-suggestion`, - type: 'index-pattern', - }, - ], - state: { - datasourceStates, - filters, - query, - visualization, - ...(dataView && - dataView.id && - !dataView.isPersisted() && { - adHocDataViews: { [dataView.id]: dataView.toSpec(false) }, - }), - }, - visualizationType: suggestion ? suggestion.visualizationId : 'lnsXY', - } as TypedLensByValueInput['attributes']; - return attributes; -}; diff --git a/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/time_shift.tsx b/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/time_shift.tsx index 8bf26114416de..fe69c9d76d45b 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/time_shift.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/time_shift.tsx @@ -155,7 +155,7 @@ export function TimeShift({ ); })} selectedOptions={getSelectedOption()} - singleSelection={{ asPlainText: true }} + singleSelection={{ asPlainText: false }} isInvalid={isLocalValueInvalid} onCreateOption={(val) => { const parsedVal = parseTimeShift(val); diff --git a/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx b/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx index d28d6fd3b527e..8540058683c11 100644 --- a/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx +++ b/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx @@ -46,7 +46,6 @@ export interface ExpressionWrapperProps { executionContext?: KibanaExecutionContext; lensInspector: LensInspector; noPadding?: boolean; - shouldUseSizeTransitionVeil?: boolean; abortController?: AbortController; } @@ -73,7 +72,6 @@ export function ExpressionWrapper({ executionContext, lensInspector, noPadding, - shouldUseSizeTransitionVeil, abortController, }: ExpressionWrapperProps) { if (!expression) return null; @@ -97,7 +95,6 @@ export function ExpressionWrapper({ syncCursor={syncCursor} executionContext={executionContext} abortController={abortController} - shouldUseSizeTransitionVeil={shouldUseSizeTransitionVeil ?? true} renderError={(errorMessage, error) => { const messages = getOriginalRequestErrorMessages(error || null); addUserMessages(messages); diff --git a/x-pack/plugins/lens/public/lens_attribute_service.ts b/x-pack/plugins/lens/public/lens_attribute_service.ts index c63d9c1dd5c3d..69b76669695fe 100644 --- a/x-pack/plugins/lens/public/lens_attribute_service.ts +++ b/x-pack/plugins/lens/public/lens_attribute_service.ts @@ -8,7 +8,9 @@ import type { CoreStart } from '@kbn/core/public'; import type { AttributeService } from '@kbn/embeddable-plugin/public'; import { OnSaveProps } from '@kbn/saved-objects-plugin/public'; +import { SavedObjectCommon } from '@kbn/saved-objects-finder-plugin/common'; import type { LensPluginStartDependencies } from './plugin'; +import type { LensSavedObjectAttributes as LensSavedObjectAttributesWithoutReferences } from '../common/content_management'; import type { LensSavedObjectAttributes, LensByValueInput, @@ -26,6 +28,16 @@ export type LensAttributeService = AttributeService< LensUnwrapMetaInfo >; +export const savedObjectToEmbeddableAttributes = ( + savedObject: SavedObjectCommon +): LensSavedObjectAttributes => { + return { + ...savedObject.attributes, + state: savedObject.attributes.state as LensSavedObjectAttributes['state'], + references: savedObject.references, + }; +}; + export function getLensAttributeService( core: CoreStart, startDependencies: LensPluginStartDependencies @@ -51,11 +63,7 @@ export function getLensAttributeService( item: savedObject, meta: { outcome, aliasTargetId, aliasPurpose }, } = await savedObjectStore.load(savedObjectId); - const { attributes, references, id } = savedObject; - const document = { - ...attributes, - references, - }; + const { id } = savedObject; const sharingSavedObjectProps = { aliasTargetId, @@ -65,10 +73,7 @@ export function getLensAttributeService( }; return { - attributes: { - ...document, - state: document.state as LensSavedObjectAttributes['state'], - }, + attributes: savedObjectToEmbeddableAttributes(savedObject), metaInfo: { sharingSavedObjectProps, managed: savedObject.managed, diff --git a/x-pack/plugins/lens/public/loader.test.tsx b/x-pack/plugins/lens/public/loader.test.tsx deleted file mode 100644 index 936edaa59f6bd..0000000000000 --- a/x-pack/plugins/lens/public/loader.test.tsx +++ /dev/null @@ -1,60 +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 { EuiProgress } from '@elastic/eui'; -import { Loader } from './loader'; -import { mount } from 'enzyme'; -import { act } from 'react-dom/test-utils'; - -describe('loader', () => { - it('shows a loading indicator when loading', async () => { - const load = jest.fn(() => Promise.resolve()); - const inst = mount(); - expect(inst.find(EuiProgress).length).toEqual(1); - - await act(async () => load()); - inst.update(); - expect(inst.find(EuiProgress).length).toEqual(0); - }); - - it('hides loading indicator when failed', async () => { - const load = jest.fn(() => Promise.reject()); - const inst = mount(); - expect(inst.find(EuiProgress).length).toEqual(1); - - await act(async () => Promise.resolve()); - inst.update(); - - expect(inst.find(EuiProgress).length).toEqual(0); - }); - - it('does not run load in parallel', async () => { - let count = 0; - let ranInParallel = false; - const load = jest.fn(() => { - if (count) { - ranInParallel = true; - } - ++count; - return Promise.resolve().then(() => --count); - }); - const inst = mount(); - - await act(async () => { - inst.setProps({ loadDeps: ['foo'] }); - }); - - inst.update(); - - await Promise.resolve(); - inst.update(); - - expect(load).toHaveBeenCalledTimes(2); - expect(ranInParallel).toBeFalsy(); - }); -}); diff --git a/x-pack/plugins/lens/public/loader.tsx b/x-pack/plugins/lens/public/loader.tsx deleted file mode 100644 index 2c274a2ff4501..0000000000000 --- a/x-pack/plugins/lens/public/loader.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useState, useEffect, useRef } from 'react'; -import { EuiProgress } from '@elastic/eui'; - -/** - * Executes the specified load function any time loadDeps changes. Ensures the load - * function is never run in parallel. Shows a loading indicator while loading. - */ -export function Loader(props: { load: () => Promise; loadDeps: unknown[] }) { - const [isProcessing, setIsProcessing] = useState(false); - const prevRequest = useRef | undefined>(undefined); - const nextRequest = useRef<(() => void) | undefined>(undefined); - - useEffect( - function performLoad() { - if (prevRequest.current) { - nextRequest.current = performLoad; - return; - } - - setIsProcessing(true); - prevRequest.current = props - .load() - .catch(() => {}) - .then(() => { - const reload = nextRequest.current; - prevRequest.current = undefined; - nextRequest.current = undefined; - - if (reload) { - reload(); - } else { - setIsProcessing(false); - } - }); - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - props.loadDeps - ); - - if (!isProcessing) { - return null; - } - - return ; -} diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index 61ffb1d0686d0..663a5d8b574c2 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -64,6 +64,7 @@ import { } from '@kbn/content-management-plugin/public'; import { i18n } from '@kbn/i18n'; import type { ServerlessPluginStart } from '@kbn/serverless/public'; +import { registerSavedObjectToPanelMethod } from '@kbn/embeddable-plugin/public'; import type { EditorFrameService as EditorFrameServiceType } from './editor_frame_service'; import type { FormBasedDatasource as FormBasedDatasourceType, @@ -117,7 +118,7 @@ import { visualizeTSVBAction } from './trigger_actions/visualize_tsvb_actions'; import { visualizeAggBasedVisAction } from './trigger_actions/visualize_agg_based_vis_actions'; import { visualizeDashboardVisualizePanelction } from './trigger_actions/dashboard_visualize_panel_actions'; -import type { LensEmbeddableInput } from './embeddable'; +import type { LensByValueInput, LensEmbeddableInput } from './embeddable'; import { EmbeddableFactory, LensEmbeddableStartServices } from './embeddable/embeddable_factory'; import { EmbeddableComponent, getEmbeddableComponent } from './embeddable/embeddable_component'; import { getSaveModalComponent } from './app_plugin/shared/saved_modal_lazy'; @@ -130,8 +131,13 @@ import { ChartInfoApi } from './chart_info_api'; import { type LensAppLocator, LensAppLocatorDefinition } from '../common/locator/locator'; import { downloadCsvShareProvider } from './app_plugin/csv_download_provider/csv_download_provider'; -import { CONTENT_ID, LATEST_VERSION } from '../common/content_management'; +import { + CONTENT_ID, + LATEST_VERSION, + LensSavedObjectAttributes, +} from '../common/content_management'; import type { EditLensConfigurationProps } from './app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration'; +import { savedObjectToEmbeddableAttributes } from './lens_attribute_service'; export type { SaveProps } from './app_plugin'; @@ -424,6 +430,21 @@ export class LensPlugin { () => startServices().plugins.data.nowProvider.get() ); + registerSavedObjectToPanelMethod( + CONTENT_ID, + (savedObject) => { + if (!savedObject.managed) { + return { savedObjectId: savedObject.id }; + } + + const panel = { + attributes: savedObjectToEmbeddableAttributes(savedObject), + }; + + return panel; + } + ); + const getPresentationUtilContext = () => startServices().plugins.presentationUtil.ContextProvider; diff --git a/x-pack/plugins/lens/public/shared_components/palette_picker.tsx b/x-pack/plugins/lens/public/shared_components/palette_picker.tsx index 51977e551128e..6796eee37a542 100644 --- a/x-pack/plugins/lens/public/shared_components/palette_picker.tsx +++ b/x-pack/plugins/lens/public/shared_components/palette_picker.tsx @@ -7,6 +7,7 @@ import React from 'react'; import type { PaletteOutput, PaletteRegistry } from '@kbn/coloring'; +import { getActivePaletteName } from '@kbn/coloring'; import { EuiColorPalettePicker, EuiColorPalettePickerPaletteProps } from '@elastic/eui'; import { EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -20,6 +21,8 @@ export function PalettePicker({ activePalette?: PaletteOutput; setPalette: (palette: PaletteOutput) => void; }) { + const paletteName = getActivePaletteName(activePalette?.name); + const palettesToShow: EuiColorPalettePickerPaletteProps[] = palettes .getAll() .filter(({ internal }) => !internal) @@ -28,10 +31,7 @@ export function PalettePicker({ value: id, title, type: 'fixed', - palette: getCategoricalColors( - 10, - id === activePalette?.name ? activePalette?.params : undefined - ), + palette: getCategoricalColors(10, id === paletteName ? activePalette?.params : undefined), }; }); return ( @@ -51,7 +51,7 @@ export function PalettePicker({ name: newPalette, }); }} - valueOfSelected={activePalette?.name || 'default'} + valueOfSelected={paletteName} selectionDisplay={'palette'} /> diff --git a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/create_action.test.tsx b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/create_action.test.tsx index 9a06fea94cd98..27372f10ce973 100644 --- a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/create_action.test.tsx +++ b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/create_action.test.tsx @@ -8,16 +8,20 @@ import type { CoreStart } from '@kbn/core/public'; import { coreMock } from '@kbn/core/public/mocks'; import type { LensPluginStartDependencies } from '../../plugin'; import { createMockStartDependencies } from '../../editor_frame_service/mocks'; +import { getMockPresentationContainer } from '@kbn/presentation-containers/mocks'; import { CreateESQLPanelAction } from './create_action'; describe('create Lens panel action', () => { const core = coreMock.createStart(); const mockStartDependencies = createMockStartDependencies() as unknown as LensPluginStartDependencies; + const mockPresentationContainer = getMockPresentationContainer(); describe('compatibility check', () => { it('is incompatible if ui setting for ES|QL is off', async () => { const configurablePanelAction = new CreateESQLPanelAction(mockStartDependencies, core); - const isCompatible = await configurablePanelAction.isCompatible(); + const isCompatible = await configurablePanelAction.isCompatible({ + embeddable: mockPresentationContainer, + }); expect(isCompatible).toBeFalsy(); }); @@ -33,7 +37,9 @@ describe('create Lens panel action', () => { }, } as CoreStart; const createESQLAction = new CreateESQLPanelAction(mockStartDependencies, updatedCore); - const isCompatible = await createESQLAction.isCompatible(); + const isCompatible = await createESQLAction.isCompatible({ + embeddable: mockPresentationContainer, + }); expect(isCompatible).toBeTruthy(); }); diff --git a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/create_action.tsx b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/create_action.tsx index aa33a629c3969..07301f2394130 100644 --- a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/create_action.tsx +++ b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/create_action.tsx @@ -6,29 +6,16 @@ */ import { i18n } from '@kbn/i18n'; import type { CoreStart } from '@kbn/core/public'; -import { Action } from '@kbn/ui-actions-plugin/public'; -import type { - EmbeddableFactory, - EmbeddableInput, - IEmbeddable, -} from '@kbn/embeddable-plugin/public'; +import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; +import { EmbeddableApiContext } from '@kbn/presentation-publishing'; +import { apiIsPresentationContainer } from '@kbn/presentation-containers'; import type { LensPluginStartDependencies } from '../../plugin'; const ACTION_CREATE_ESQL_CHART = 'ACTION_CREATE_ESQL_CHART'; -interface Context { - createNewEmbeddable: ( - embeddableFactory: EmbeddableFactory, - initialInput?: Partial, - dismissNotification?: boolean - ) => Promise; - deleteEmbeddable: (embeddableId: string) => void; - initialInput?: Partial; -} - export const getAsyncHelpers = async () => await import('../../async_services'); -export class CreateESQLPanelAction implements Action { +export class CreateESQLPanelAction implements Action { public type = ACTION_CREATE_ESQL_CHART; public id = ACTION_CREATE_ESQL_CHART; public order = 50; @@ -49,19 +36,20 @@ export class CreateESQLPanelAction implements Action { return 'esqlVis'; } - public async isCompatible() { + public async isCompatible({ embeddable }: EmbeddableApiContext) { + if (!apiIsPresentationContainer(embeddable)) return false; // compatible only when ES|QL advanced setting is enabled const { isCreateActionCompatible } = await getAsyncHelpers(); return isCreateActionCompatible(this.core); } - public async execute({ createNewEmbeddable, deleteEmbeddable }: Context) { + public async execute({ embeddable }: EmbeddableApiContext) { + if (!apiIsPresentationContainer(embeddable)) throw new IncompatibleActionError(); const { executeCreateAction } = await getAsyncHelpers(); executeCreateAction({ deps: this.startDependencies, core: this.core, - createNewEmbeddable, - deleteEmbeddable, + api: embeddable, }); } } diff --git a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/create_action_helpers.ts b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/create_action_helpers.ts index 7fb732cd72ccf..49ce940743e70 100644 --- a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/create_action_helpers.ts +++ b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/create_action_helpers.ts @@ -6,20 +6,17 @@ */ import { createGetterSetter } from '@kbn/kibana-utils-plugin/common'; import type { CoreStart } from '@kbn/core/public'; -import type { - EmbeddableFactory, - EmbeddableInput, - IEmbeddable, -} from '@kbn/embeddable-plugin/public'; +import { getLensAttributesFromSuggestion } from '@kbn/visualization-utils'; import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; +import { PresentationContainer } from '@kbn/presentation-containers'; import { getESQLAdHocDataview, getIndexForESQLQuery } from '@kbn/esql-utils'; import type { Datasource, Visualization } from '../../types'; import type { LensPluginStartDependencies } from '../../plugin'; import { fetchDataFromAggregateQuery } from '../../datasources/text_based/fetch_data_from_aggregate_query'; import { suggestionsApi } from '../../lens_suggestions_api'; -import { getLensAttributes } from '../../app_plugin/shared/edit_on_the_fly/helpers'; import { generateId } from '../../id_generator'; import { executeEditAction } from './edit_action_helpers'; +import { Embeddable } from '../../embeddable'; // datasourceMap and visualizationMap setters/getters export const [getVisualizationMap, setVisualizationMap] = createGetterSetter< @@ -37,17 +34,11 @@ export function isCreateActionCompatible(core: CoreStart) { export async function executeCreateAction({ deps, core, - createNewEmbeddable, - deleteEmbeddable, + api, }: { deps: LensPluginStartDependencies; core: CoreStart; - createNewEmbeddable: ( - embeddableFactory: EmbeddableFactory, - initialInput?: Partial, - dismissNotification?: boolean - ) => Promise; - deleteEmbeddable: (embeddableId: string) => void; + api: PresentationContainer; }) { const isCompatibleAction = isCreateActionCompatible(core); const defaultDataView = await deps.dataViews.getDefaultDataView({ @@ -103,27 +94,24 @@ export async function executeCreateAction({ // Lens might not return suggestions for some cases, i.e. in case of errors if (!allSuggestions.length) return undefined; const [firstSuggestion] = allSuggestions; - const attrs = getLensAttributes({ + const attrs = getLensAttributesFromSuggestion({ filters: [], query: defaultEsqlQuery, suggestion: firstSuggestion, dataView, }); - const input = { - attributes: attrs, - id: generateId(), - }; - const embeddableStart = deps.embeddable; - const factory = embeddableStart.getEmbeddableFactory('lens'); - if (!factory) { - return undefined; - } - const embeddable = await createNewEmbeddable(factory, input, true); + const embeddable = await api.addNewPanel({ + panelType: 'lens', + initialState: { + attributes: attrs, + id: generateId(), + }, + }); // open the flyout if embeddable has been created successfully if (embeddable) { const deletePanel = () => { - deleteEmbeddable(embeddable.id); + api.removePanel(embeddable.id); }; executeEditAction({ diff --git a/x-pack/plugins/lens/tsconfig.json b/x-pack/plugins/lens/tsconfig.json index 779aa6886b05e..73883bc849e27 100644 --- a/x-pack/plugins/lens/tsconfig.json +++ b/x-pack/plugins/lens/tsconfig.json @@ -109,7 +109,8 @@ "@kbn/text-based-editor", "@kbn/managed-content-badge", "@kbn/sort-predicates", - "@kbn/presentation-publishing" + "@kbn/presentation-publishing", + "@kbn/saved-objects-finder-plugin" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/lists/server/routes/list/import_list_item_route.ts b/x-pack/plugins/lists/server/routes/list/import_list_item_route.ts index 822a18fd13cf1..9e64fe59404c6 100644 --- a/x-pack/plugins/lists/server/routes/list/import_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/list/import_list_item_route.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { extname } from 'path'; + import { schema } from '@kbn/config-schema'; import { validate } from '@kbn/securitysolution-io-ts-utils'; import { transformError } from '@kbn/securitysolution-es-utils'; @@ -17,6 +19,8 @@ import { buildRouteValidation, buildSiemResponse } from '../utils'; import { createStreamFromBuffer } from '../utils/create_stream_from_buffer'; import { getListClient } from '..'; +const validFileExtensions = ['.csv', '.txt']; + export const importListItemRoute = (router: ListsPluginRouter, config: ConfigType): void => { router.versioned .post({ @@ -47,10 +51,29 @@ export const importListItemRoute = (router: ListsPluginRouter, config: ConfigTyp async (context, request, response) => { const siemResponse = buildSiemResponse(response); try { - const stream = createStreamFromBuffer(request.body); const { deserializer, list_id: listId, serializer, type } = request.query; const lists = await getListClient(context); + const filename = await lists.getImportFilename({ + stream: createStreamFromBuffer(request.body), + }); + if (!filename) { + return siemResponse.error({ + body: 'To import a list item, the file name must be specified', + statusCode: 400, + }); + } + const fileExtension = extname(filename).toLowerCase(); + if (!validFileExtensions.includes(fileExtension)) { + return siemResponse.error({ + body: `Unsupported media type. File must be one of the following types: [${validFileExtensions.join( + ', ' + )}]`, + statusCode: 415, + }); + } + + const stream = createStreamFromBuffer(request.body); const listDataExists = await lists.getListDataStreamExists(); if (!listDataExists) { const listIndexExists = await lists.getListIndexExists(); diff --git a/x-pack/plugins/lists/server/services/lists/list_client.ts b/x-pack/plugins/lists/server/services/lists/list_client.ts index 826e4a545d80d..d1d5c585c3f85 100644 --- a/x-pack/plugins/lists/server/services/lists/list_client.ts +++ b/x-pack/plugins/lists/server/services/lists/list_client.ts @@ -37,6 +37,7 @@ import type { import type { ConfigType } from '../../config'; import { + BufferLines, createListItem, deleteListItem, deleteListItemByValue, @@ -69,6 +70,7 @@ import type { FindAllListItemsOptions, FindListItemOptions, FindListOptions, + GetImportFilename, GetListItemByValueOptions, GetListItemOptions, GetListItemsByValueOptions, @@ -715,6 +717,33 @@ export class ListClient { }); }; + /** + * Gets the filename of the imported file + * @param options + * @param options.stream The stream to pull the import from + * @returns + */ + public getImportFilename = ({ stream }: GetImportFilename): Promise => { + return new Promise((resolve, reject) => { + const { config } = this; + const readBuffer = new BufferLines({ bufferSize: config.importBufferSize, input: stream }); + let fileName: string | undefined; + readBuffer.on('fileName', async (fileNameEmitted: string) => { + try { + readBuffer.pause(); + fileName = decodeURIComponent(fileNameEmitted); + readBuffer.resume(); + } catch (err) { + reject(err); + } + }); + + readBuffer.on('close', () => { + resolve(fileName); + }); + }); + }; + /** * Imports list items to a stream. If the list already exists, this will append the list items to the existing list. * If the list does not exist, this will auto-create the list and then add the items to that list. diff --git a/x-pack/plugins/lists/server/services/lists/list_client_types.ts b/x-pack/plugins/lists/server/services/lists/list_client_types.ts index 7509eeb914241..4c64e8e940163 100644 --- a/x-pack/plugins/lists/server/services/lists/list_client_types.ts +++ b/x-pack/plugins/lists/server/services/lists/list_client_types.ts @@ -335,3 +335,12 @@ export interface SearchListItemByValuesOptions { /** The value to search for list items based off. */ value: unknown[]; } + +/** + * ListClient.getImportFilename + * {@link ListClient.getImportFilename} + */ +export interface GetImportFilename { + /** The stream to pull the import from */ + stream: Readable; +} diff --git a/x-pack/plugins/maps/public/classes/sources/esql_source/convert_to_geojson.ts b/x-pack/plugins/maps/public/classes/sources/esql_source/convert_to_geojson.ts index 3940cd9102c54..a446f976b5677 100644 --- a/x-pack/plugins/maps/public/classes/sources/esql_source/convert_to_geojson.ts +++ b/x-pack/plugins/maps/public/classes/sources/esql_source/convert_to_geojson.ts @@ -9,14 +9,19 @@ import { parse } from 'wellknown'; import { Feature, FeatureCollection, GeoJsonProperties } from 'geojson'; import type { ESQLSearchReponse } from '@kbn/es-types'; -import { getGeometryColumnIndex } from './esql_utils'; +import { EMPTY_FEATURE_COLLECTION } from '../../../../common/constants'; +import { isGeometryColumn } from './esql_utils'; export function convertToGeoJson(resp: ESQLSearchReponse): FeatureCollection { - const geometryIndex = getGeometryColumnIndex(resp.columns); + const geometryColumnIndex = resp.columns.findIndex(isGeometryColumn); + if (geometryColumnIndex === -1) { + return EMPTY_FEATURE_COLLECTION; + } + const features: Feature[] = []; for (let i = 0; i < resp.values.length; i++) { const hit = resp.values[i]; - const wkt = hit[geometryIndex]; + const wkt = hit[geometryColumnIndex]; if (!wkt) { continue; } @@ -25,7 +30,7 @@ export function convertToGeoJson(resp: ESQLSearchReponse): FeatureCollection { const properties: GeoJsonProperties = {}; for (let j = 0; j < hit.length; j++) { // do not store geometry in properties - if (j === geometryIndex) { + if (j === geometryColumnIndex) { continue; } properties[resp.columns[j].name] = hit[j] as unknown; diff --git a/x-pack/plugins/maps/public/classes/sources/esql_source/esql_source.tsx b/x-pack/plugins/maps/public/classes/sources/esql_source/esql_source.tsx index be5cdea7c7fbf..d438a714beb40 100644 --- a/x-pack/plugins/maps/public/classes/sources/esql_source/esql_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/esql_source/esql_source.tsx @@ -13,8 +13,8 @@ import { v4 as uuidv4 } from 'uuid'; import { Adapters } from '@kbn/inspector-plugin/common/adapters'; import { getIndexPatternFromESQLQuery, getLimitFromESQLQuery } from '@kbn/esql-utils'; import { buildEsQuery } from '@kbn/es-query'; -import type { BoolQuery, Filter, Query } from '@kbn/es-query'; -import type { ESQLSearchReponse } from '@kbn/es-types'; +import type { Filter, Query } from '@kbn/es-query'; +import type { ESQLSearchParams, ESQLSearchReponse } from '@kbn/es-types'; import { getEsQueryConfig } from '@kbn/data-service/src/es_query'; import { getTime } from '@kbn/data-plugin/public'; import { FIELD_ORIGIN, SOURCE_TYPES, VECTOR_SHAPE_TYPE } from '../../../../common/constants'; @@ -32,12 +32,7 @@ import type { IField } from '../../fields/field'; import { InlineField } from '../../fields/inline_field'; import { getData, getUiSettings } from '../../../kibana_services'; import { convertToGeoJson } from './convert_to_geojson'; -import { - getFieldType, - getGeometryColumnIndex, - ESQL_GEO_POINT_TYPE, - ESQL_GEO_SHAPE_TYPE, -} from './esql_utils'; +import { getFieldType, isGeometryColumn, ESQL_GEO_SHAPE_TYPE } from './esql_utils'; import { UpdateSourceEditor } from './update_source_editor'; type ESQLSourceSyncMeta = Pick< @@ -128,16 +123,8 @@ export class ESQLSource extends AbstractVectorSource implements IVectorSource { } async getSupportedShapeTypes() { - let geomtryColumnType = ESQL_GEO_POINT_TYPE; - try { - const index = getGeometryColumnIndex(this._descriptor.columns); - if (index > -1) { - geomtryColumnType = this._descriptor.columns[index].type; - } - } catch (error) { - // errors for missing geometry columns surfaced in UI by data loading - } - return geomtryColumnType === ESQL_GEO_SHAPE_TYPE + const index = this._descriptor.columns.findIndex(isGeometryColumn); + return index !== -1 && this._descriptor.columns[index].type === ESQL_GEO_SHAPE_TYPE ? [VECTOR_SHAPE_TYPE.POINT, VECTOR_SHAPE_TYPE.LINE, VECTOR_SHAPE_TYPE.POLYGON] : [VECTOR_SHAPE_TYPE.POINT]; } @@ -154,8 +141,9 @@ export class ESQLSource extends AbstractVectorSource implements IVectorSource { inspectorAdapters: Adapters ): Promise { const limit = getLimitFromESQLQuery(this._descriptor.esql); - const params: { query: string; filter?: { bool: BoolQuery } } = { + const params: ESQLSearchParams = { query: this._descriptor.esql, + dropNullColumns: true, }; const query: Query[] = []; diff --git a/x-pack/plugins/maps/public/classes/sources/esql_source/esql_utils.ts b/x-pack/plugins/maps/public/classes/sources/esql_source/esql_utils.ts index 7a4b5048c820a..c247170874ba3 100644 --- a/x-pack/plugins/maps/public/classes/sources/esql_source/esql_utils.ts +++ b/x-pack/plugins/maps/public/classes/sources/esql_source/esql_utils.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { lastValueFrom } from 'rxjs'; import { getIndexPatternFromESQLQuery } from '@kbn/esql-utils'; -import type { ESQLColumn } from '@kbn/es-types'; +import type { ESQLColumn, ESQLSearchReponse } from '@kbn/es-types'; import { ES_GEO_FIELD_TYPE } from '../../../../common/constants'; import { getData, getIndexPatternService } from '../../../kibana_services'; @@ -22,13 +22,6 @@ export const ESQL_GEO_POINT_TYPE = 'geo_point'; // ESQL_GEO_SHAPE_TYPE is a column type from an ESQL response export const ESQL_GEO_SHAPE_TYPE = 'geo_shape'; -const NO_GEOMETRY_COLUMN_ERROR_MSG = i18n.translate( - 'xpack.maps.source.esql.noGeometryColumnErrorMsg', - { - defaultMessage: 'Elasticsearch ES|QL query does not have a geometry column.', - } -); - export function isGeometryColumn(column: ESQLColumn) { return [ESQL_GEO_POINT_TYPE, ESQL_GEO_SHAPE_TYPE].includes(column.type); } @@ -36,7 +29,11 @@ export function isGeometryColumn(column: ESQLColumn) { export function verifyGeometryColumn(columns: ESQLColumn[]) { const geometryColumns = columns.filter(isGeometryColumn); if (geometryColumns.length === 0) { - throw new Error(NO_GEOMETRY_COLUMN_ERROR_MSG); + throw new Error( + i18n.translate('xpack.maps.source.esql.noGeometryColumnErrorMsg', { + defaultMessage: 'Elasticsearch ES|QL query does not have a geometry column.', + }) + ); } if (geometryColumns.length > 1) { @@ -51,14 +48,6 @@ export function verifyGeometryColumn(columns: ESQLColumn[]) { } } -export function getGeometryColumnIndex(columns: ESQLColumn[]) { - const index = columns.findIndex(isGeometryColumn); - if (index === -1) { - throw new Error(NO_GEOMETRY_COLUMN_ERROR_MSG); - } - return index; -} - export async function getESQLMeta(esql: string) { const fields = await getFields(esql); return { @@ -104,7 +93,8 @@ async function getColumns(esql: string) { ) ); - return (resp.rawResponse as unknown as { columns: ESQLColumn[] }).columns; + const searchResponse = resp.rawResponse as unknown as ESQLSearchReponse; + return searchResponse.all_columns ? searchResponse.all_columns : searchResponse.columns; } catch (error) { throw new Error( i18n.translate('xpack.maps.source.esql.getColumnsErrorMsg', { diff --git a/x-pack/plugins/maps/public/map_attribute_service.ts b/x-pack/plugins/maps/public/map_attribute_service.ts index 6cc226b72dc4b..5f07f3954eecd 100644 --- a/x-pack/plugins/maps/public/map_attribute_service.ts +++ b/x-pack/plugins/maps/public/map_attribute_service.ts @@ -9,6 +9,7 @@ import { SavedObjectReference } from '@kbn/core/types'; import type { ResolvedSimpleSavedObject } from '@kbn/core/public'; import { AttributeService } from '@kbn/embeddable-plugin/public'; import type { OnSaveProps } from '@kbn/saved-objects-plugin/public'; +import { SavedObjectCommon } from '@kbn/saved-objects-finder-plugin/common'; import type { MapAttributes } from '../common/content_management'; import { MAP_EMBEDDABLE_NAME, MAP_SAVED_OBJECT_TYPE } from '../common/constants'; import { getCoreOverlays, getEmbeddableService } from './kibana_services'; @@ -39,6 +40,17 @@ export type MapAttributeService = AttributeService< MapUnwrapMetaInfo >; +export const savedObjectToEmbeddableAttributes = ( + savedObject: SavedObjectCommon +) => { + const { attributes } = injectReferences(savedObject); + + return { + ...attributes, + references: savedObject.references, + }; +}; + let mapAttributeService: MapAttributeService | null = null; export function getMapAttributeService(): MapAttributeService { @@ -90,12 +102,8 @@ export function getMapAttributeService(): MapAttributeService { throw savedObject.error; } - const { attributes } = injectReferences(savedObject); return { - attributes: { - ...attributes, - references: savedObject.references, - }, + attributes: savedObjectToEmbeddableAttributes(savedObject), metaInfo: { sharingSavedObjectProps: { aliasTargetId, diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index 4e654f9e5c641..351f49fcfd077 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -25,7 +25,11 @@ import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; import type { VisualizationsSetup, VisualizationsStart } from '@kbn/visualizations-plugin/public'; import type { Plugin as ExpressionsPublicPlugin } from '@kbn/expressions-plugin/public'; import { VISUALIZE_GEO_FIELD_TRIGGER } from '@kbn/ui-actions-plugin/public'; -import type { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public'; +import { + EmbeddableSetup, + EmbeddableStart, + registerSavedObjectToPanelMethod, +} from '@kbn/embeddable-plugin/public'; import { CONTEXT_MENU_TRIGGER } from '@kbn/embeddable-plugin/public'; import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; import type { MapsEmsPluginPublicStart } from '@kbn/maps-ems-plugin/public'; @@ -82,7 +86,9 @@ import { MapInspectorView } from './inspector/map_adapter/map_inspector_view'; import { VectorTileInspectorView } from './inspector/vector_tile_adapter/vector_tile_inspector_view'; import { setupLensChoroplethChart } from './lens'; -import { CONTENT_ID, LATEST_VERSION } from '../common/content_management'; +import { CONTENT_ID, LATEST_VERSION, MapAttributes } from '../common/content_management'; +import { savedObjectToEmbeddableAttributes } from './map_attribute_service'; +import { MapByValueInput } from './embeddable'; export interface MapsPluginSetupDependencies { cloud?: CloudSetup; @@ -214,6 +220,16 @@ export class MapsPlugin name: APP_NAME, }); + registerSavedObjectToPanelMethod(CONTENT_ID, (savedObject) => { + if (!savedObject.managed) { + return { savedObjectId: savedObject.id }; + } + + return { + attributes: savedObjectToEmbeddableAttributes(savedObject), + }; + }); + setupLensChoroplethChart(core, plugins.expressions, plugins.lens); // register wrapper around legacy tile_map and region_map visualizations diff --git a/x-pack/plugins/maps/tsconfig.json b/x-pack/plugins/maps/tsconfig.json index 8dcb5e3db7a26..27cdf35bfb949 100644 --- a/x-pack/plugins/maps/tsconfig.json +++ b/x-pack/plugins/maps/tsconfig.json @@ -85,6 +85,7 @@ "@kbn/code-editor", "@kbn/managed-content-badge", "@kbn/presentation-publishing", + "@kbn/saved-objects-finder-plugin", "@kbn/esql-utils", ], "exclude": [ diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table.js b/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table.js index 9b023ae640c10..0edb008184aae 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table.js +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table.js @@ -144,9 +144,8 @@ export class AnomaliesTableInternal extends Component { }; unsetShowRuleEditorFlyoutFunction = () => { - const showRuleEditorFlyout = () => {}; this.setState({ - showRuleEditorFlyout, + showRuleEditorFlyout: () => {}, }); }; diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js b/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js index 0e21de91dbbb9..abb7055b41a8f 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js @@ -83,11 +83,13 @@ class RuleEditorFlyoutUI extends Component { } componentDidMount() { - this.toastNotificationService = toastNotificationServiceProvider( - this.props.kibana.services.notifications.toasts - ); - if (typeof this.props.setShowFunction === 'function') { - this.props.setShowFunction(this.showFlyout); + if (this.props.kibana.services.notifications) { + this.toastNotificationService = toastNotificationServiceProvider( + this.props.kibana.services.notifications.toasts + ); + if (typeof this.props.setShowFunction === 'function') { + this.props.setShowFunction(this.showFlyout); + } } } @@ -480,7 +482,7 @@ class RuleEditorFlyoutUI extends Component { }; render() { - const docsUrl = this.props.kibana.services.docLinks.links.ml.customRules; + const docsUrl = this.props.kibana.services.docLinks?.links.ml.customRules; const { isFlyoutVisible, job, diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer.scss b/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer.scss index 6160db3b940c8..e47e69c741a90 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer.scss +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer.scss @@ -74,6 +74,11 @@ pointer-events: none; } + .values-dots circle { + fill: $euiColorPrimary; + stroke-width: 0; + } + .metric-value { opacity: 1; fill: transparent; diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js index a66a5f0efece2..b459f0bffcee0 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js @@ -17,6 +17,8 @@ import { isEqual, reduce, each, get } from 'lodash'; import d3 from 'd3'; import moment from 'moment'; +import { EuiPopover } from '@elastic/eui'; + import { i18n } from '@kbn/i18n'; import { getFormattedSeverityScore, getSeverityWithLow } from '@kbn/ml-anomaly-utils'; import { formatHumanReadableDateTimeSeconds } from '@kbn/ml-date-utils'; @@ -52,6 +54,9 @@ import { } from './timeseries_chart_annotations'; import { MlAnnotationUpdatesContext } from '../../../contexts/ml/ml_annotation_updates_context'; +import { LinksMenuUI } from '../../../components/anomalies_table/links_menu'; +import { RuleEditorFlyout } from '../../../components/rule_editor'; + const focusZoomPanelHeight = 25; const focusChartHeight = 310; const focusHeight = focusZoomPanelHeight + focusChartHeight; @@ -60,6 +65,7 @@ const contextChartLineTopMargin = 3; const chartSpacing = 25; const swimlaneHeight = 30; const ctxAnnotationMargin = 2; +const popoverMenuOffset = 28; const annotationHeight = ANNOTATION_SYMBOL_HEIGHT + ctxAnnotationMargin * 2; const margin = { top: 10, right: 10, bottom: 15, left: 40 }; @@ -123,11 +129,18 @@ class TimeseriesChartIntl extends Component { zoomFromFocusLoaded: PropTypes.object, zoomToFocusLoaded: PropTypes.object, tooltipService: PropTypes.object.isRequired, + tableData: PropTypes.object, + sourceIndicesWithGeoFields: PropTypes.object.isRequired, }; rowMouseenterSubscriber = null; rowMouseleaveSubscriber = null; + constructor(props) { + super(props); + this.state = { popoverData: null, popoverCoords: [0, 0], showRuleEditorFlyout: () => {} }; + } + componentWillUnmount() { const element = d3.select(this.rootNode); element.html(''); @@ -206,7 +219,10 @@ class TimeseriesChartIntl extends Component { const highlightFocusChartAnomaly = this.highlightFocusChartAnomaly.bind(this); const boundHighlightFocusChartAnnotation = highlightFocusChartAnnotation.bind(this); function tableRecordMousenterListener({ record, type = 'anomaly' }) { - if (type === 'anomaly') { + // do not display tooltips if the action popover is active + if (this.state.popoverData !== null) { + return; + } else if (type === 'anomaly') { highlightFocusChartAnomaly(record); } else if (type === 'annotation') { boundHighlightFocusChartAnnotation(record); @@ -217,7 +233,7 @@ class TimeseriesChartIntl extends Component { const boundUnhighlightFocusChartAnnotation = unhighlightFocusChartAnnotation.bind(this); function tableRecordMouseleaveListener({ record, type = 'anomaly' }) { if (type === 'anomaly') { - unhighlightFocusChartAnomaly(record); + unhighlightFocusChartAnomaly(); } else { boundUnhighlightFocusChartAnnotation(record); } @@ -594,8 +610,8 @@ class TimeseriesChartIntl extends Component { const data = focusChartData; const contextYScale = this.contextYScale; + const showAnomalyPopover = this.showAnomalyPopover.bind(this); const showFocusChartTooltip = this.showFocusChartTooltip.bind(this); - const hideFocusChartTooltip = this.props.tooltipService.hide.bind(this.props.tooltipService); const focusChart = d3.select('.focus-chart'); @@ -766,6 +782,8 @@ class TimeseriesChartIntl extends Component { ) ); + const that = this; + // Remove dots that are no longer needed i.e. if number of chart points has decreased. dots.exit().remove(); // Create any new dots that are needed i.e. if number of chart points has increased. @@ -773,8 +791,16 @@ class TimeseriesChartIntl extends Component { .enter() .append('circle') .attr('r', LINE_CHART_ANOMALY_RADIUS) + .on('click', function (d) { + d3.event.preventDefault(); + if (d.anomalyScore === undefined) return; + showAnomalyPopover(d, this); + }) .on('mouseover', function (d) { - showFocusChartTooltip(d, this); + // Show the tooltip only if the actions menu isn't active + if (that.state.popoverData === null) { + showFocusChartTooltip(d, this); + } }) .on('mouseout', () => this.props.tooltipService.hide()); @@ -786,6 +812,7 @@ class TimeseriesChartIntl extends Component { .attr('cy', (d) => { return this.focusYScale(d.value); }) + .attr('data-test-subj', (d) => (d.anomalyScore !== undefined ? 'mlAnomalyMarker' : undefined)) .attr('class', (d) => { let markerClass = 'metric-value'; if (d.anomalyScore !== undefined) { @@ -810,6 +837,11 @@ class TimeseriesChartIntl extends Component { .enter() .append('path') .attr('d', d3.svg.symbol().size(MULTI_BUCKET_SYMBOL_SIZE).type('cross')) + .on('click', function (d) { + d3.event.preventDefault(); + if (d.anomalyScore === undefined) return; + showAnomalyPopover(d, this); + }) .on('mouseover', function (d) { showFocusChartTooltip(d, this); }) @@ -821,6 +853,7 @@ class TimeseriesChartIntl extends Component { 'transform', (d) => `translate(${this.focusXScale(d.date)}, ${this.focusYScale(d.value)})` ) + .attr('data-test-subj', 'mlAnomalyMarker') .attr('class', (d) => `anomaly-marker multi-bucket ${getSeverityWithLow(d.anomalyScore).id}`); // Add rectangular markers for any scheduled events. @@ -1479,6 +1512,37 @@ class TimeseriesChartIntl extends Component { this.setContextBrushExtent(new Date(from), new Date(to)); } + showAnomalyPopover(marker, circle) { + const anomalyTime = marker.date.getTime(); + + // The table items could be aggregated, so we have to find the item + // that has the closest timestamp to the selected anomaly from the chart. + const tableItem = this.props.tableData.anomalies.reduce((closestItem, currentItem) => { + const closestItemDelta = Math.abs(anomalyTime - closestItem.source.timestamp); + const currentItemDelta = Math.abs(anomalyTime - currentItem.source.timestamp); + return currentItemDelta < closestItemDelta ? currentItem : closestItem; + }, this.props.tableData.anomalies[0]); + + if (tableItem) { + // Overwrite the timestamp of the possibly aggregated table item with the + // timestamp of the anomaly clicked in the chart so we're able to pick + // the right baseline and deviation time ranges for Log Rate Analysis. + tableItem.source.timestamp = anomalyTime; + + // Calculate the relative coordinates of the clicked anomaly marker + // so we're able to position the popover actions menu above it. + const dotRect = circle.getBoundingClientRect(); + const rootRect = this.rootNode.getBoundingClientRect(); + const x = Math.round(dotRect.x + dotRect.width / 2 - rootRect.x); + const y = Math.round(dotRect.y + dotRect.height / 2 - rootRect.y) - popoverMenuOffset; + + // Hide any active tooltip + this.props.tooltipService.hide(); + // Set the popover state to enable the actions menu + this.setState({ popoverData: tableItem, popoverCoords: [x, y] }); + } + } + showFocusChartTooltip(marker, circle) { const { modelPlotEnabled } = this.props; @@ -1818,6 +1882,7 @@ class TimeseriesChartIntl extends Component { .append('path') .attr('d', d3.svg.symbol().size(MULTI_BUCKET_SYMBOL_SIZE).type('cross')) .attr('transform', (d) => `translate(${focusXScale(d.date)}, ${focusYScale(d.value)})`) + .attr('data-test-subj', 'mlAnomalyMarker') .attr( 'class', (d) => @@ -1830,6 +1895,7 @@ class TimeseriesChartIntl extends Component { .attr('r', LINE_CHART_ANOMALY_RADIUS) .attr('cx', (d) => focusXScale(d.date)) .attr('cy', (d) => focusYScale(d.value)) + .attr('data-test-subj', 'mlAnomalyMarker') .attr( 'class', (d) => @@ -1862,8 +1928,60 @@ class TimeseriesChartIntl extends Component { this.rootNode = componentNode; } + closePopover() { + this.setState({ popoverData: null, popoverCoords: [0, 0] }); + } + + setShowRuleEditorFlyoutFunction = (func) => { + this.setState({ + showRuleEditorFlyout: func, + }); + }; + + unsetShowRuleEditorFlyoutFunction = () => { + this.setState({ + showRuleEditorFlyout: () => {}, + }); + }; + render() { - return
; + return ( + <> + + {this.state.popoverData !== null && ( +
+ this.closePopover()} + panelPaddingSize="none" + anchorPosition="upLeft" + > + this.closePopover()} + sourceIndicesWithGeoFields={this.props.sourceIndicesWithGeoFields} + /> + +
+ )} +
+ + ); } } @@ -1874,6 +1992,7 @@ export const TimeseriesChart = (props) => { if (annotationProp === undefined) { return null; } + return ( { const wrapper = mountWithIntl(); - expect(wrapper.html()).toBe(`
`); + expect(wrapper.html()).toBe('
'); }); }); diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_with_tooltip.tsx b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_with_tooltip.tsx index af42229d8ac79..66da1e4222887 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_with_tooltip.tsx +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_with_tooltip.tsx @@ -8,6 +8,7 @@ import React, { FC, useEffect, useState, useCallback, useContext } from 'react'; import { i18n } from '@kbn/i18n'; import { extractErrorMessage } from '@kbn/ml-error-utils'; +import type { MlAnomaliesTableRecord } from '@kbn/ml-anomaly-utils'; import { MlTooltipComponent } from '../../../components/chart_tooltip'; import { TimeseriesChart } from './timeseries_chart'; import { CombinedJob } from '../../../../../common/types/anomaly_detection_jobs'; @@ -17,6 +18,7 @@ import { useMlKibana, useNotifications } from '../../../contexts/kibana'; import { getBoundsRoundedToInterval } from '../../../util/time_buckets'; import { getControlsForDetector } from '../../get_controls_for_detector'; import { MlAnnotationUpdatesContext } from '../../../contexts/ml/ml_annotation_updates_context'; +import { SourceIndicesWithGeoFields } from '../../../explorer/explorer_utils'; interface TimeSeriesChartWithTooltipsProps { bounds: any; @@ -30,6 +32,11 @@ interface TimeSeriesChartWithTooltipsProps { chartProps: any; lastRefresh: number; contextAggregationInterval: any; + tableData?: { + anomalies: MlAnomaliesTableRecord[]; + interval: string; + }; + sourceIndicesWithGeoFields: SourceIndicesWithGeoFields; } export const TimeSeriesChartWithTooltips: FC = ({ bounds, @@ -43,6 +50,11 @@ export const TimeSeriesChartWithTooltips: FC = chartProps, lastRefresh, contextAggregationInterval, + tableData = { + anomalies: [], + interval: 'second', + }, + sourceIndicesWithGeoFields, }) => { const { toasts: toastNotifications } = useNotifications(); const { @@ -132,6 +144,8 @@ export const TimeSeriesChartWithTooltips: FC = showForecast={showForecast} showModelBounds={showModelBounds} tooltipService={tooltipService} + tableData={tableData} + sourceIndicesWithGeoFields={sourceIndicesWithGeoFields} /> )} diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js index b7b8b7fe6e77b..757f4cb06543e 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js @@ -1218,6 +1218,8 @@ export class TimeSeriesExplorer extends React.Component { showForecast={showForecast} showModelBounds={showModelBounds} lastRefresh={lastRefresh} + tableData={tableData} + sourceIndicesWithGeoFields={sourceIndicesWithGeoFields} /> {focusAnnotationError !== undefined && ( <> @@ -1316,7 +1318,7 @@ export class TimeSeriesExplorer extends React.Component { bounds={bounds} tableData={tableData} filter={this.tableFilter} - sourceIndicesWithGeoFields={sourceIndicesWithGeoFields} + sourceIndicesWithGeoFields={this.state.sourceIndicesWithGeoFields} selectedJobs={[ { id: selectedJob.job_id, diff --git a/x-pack/plugins/observability/common/constants.ts b/x-pack/plugins/observability/common/constants.ts index b10eafd3e608f..97d3d1d9eb938 100644 --- a/x-pack/plugins/observability/common/constants.ts +++ b/x-pack/plugins/observability/common/constants.ts @@ -62,3 +62,5 @@ export const observabilityRuleCreationValidConsumers: RuleCreationValidConsumer[ AlertConsumers.LOGS, AlertConsumers.OBSERVABILITY, ]; + +export const EventsAsUnit = 'events'; diff --git a/x-pack/plugins/observability/common/custom_threshold_rule/metric_value_formatter.ts b/x-pack/plugins/observability/common/custom_threshold_rule/metric_value_formatter.ts index 114f30fd85307..6d013fb038497 100644 --- a/x-pack/plugins/observability/common/custom_threshold_rule/metric_value_formatter.ts +++ b/x-pack/plugins/observability/common/custom_threshold_rule/metric_value_formatter.ts @@ -16,9 +16,9 @@ export const metricValueFormatter = (value: number | null, metric: string = '') } ); - const formatter = metric.endsWith('.pct') - ? createFormatter('percent') - : createFormatter('highPrecision'); + let formatter = createFormatter('highPrecision'); + if (metric.endsWith('.pct')) formatter = createFormatter('percent'); + if (metric.endsWith('.bytes')) formatter = createFormatter('bytes'); return value == null ? noDataValue : formatter(value); }; diff --git a/x-pack/plugins/observability/common/custom_threshold_rule/types.ts b/x-pack/plugins/observability/common/custom_threshold_rule/types.ts index 67849df1b59d7..d9943d539d21f 100644 --- a/x-pack/plugins/observability/common/custom_threshold_rule/types.ts +++ b/x-pack/plugins/observability/common/custom_threshold_rule/types.ts @@ -35,6 +35,9 @@ export enum Aggregators { MIN = 'min', MAX = 'max', CARDINALITY = 'cardinality', + RATE = 'rate', + P95 = 'p95', + P99 = 'p99', } export const aggType = fromEnum('Aggregators', Aggregators); export type AggType = rt.TypeOf; diff --git a/x-pack/plugins/observability/public/components/burn_rate_rule_editor/burn_rate_rule_editor.tsx b/x-pack/plugins/observability/public/components/burn_rate_rule_editor/burn_rate_rule_editor.tsx index 4579640c6a5ed..70451de2cc9e6 100644 --- a/x-pack/plugins/observability/public/components/burn_rate_rule_editor/burn_rate_rule_editor.tsx +++ b/x-pack/plugins/observability/public/components/burn_rate_rule_editor/burn_rate_rule_editor.tsx @@ -16,12 +16,6 @@ import { BurnRateRuleParams, WindowSchema } from '../../typings'; import { SloSelector } from './slo_selector'; import { ValidationBurnRateRuleResult } from './validation'; import { createNewWindow, Windows } from './windows'; -import { - ALERT_ACTION, - HIGH_PRIORITY_ACTION, - LOW_PRIORITY_ACTION, - MEDIUM_PRIORITY_ACTION, -} from '../../../common/constants'; import { BURN_RATE_DEFAULTS } from './constants'; import { AlertTimeTable } from './alert_time_table'; @@ -38,54 +32,25 @@ export function BurnRateRuleEditor(props: Props) { }); const [selectedSlo, setSelectedSlo] = useState(undefined); + const [windowDefs, setWindowDefs] = useState(ruleParams?.windows || []); useEffect(() => { setSelectedSlo(initialSlo); + setWindowDefs((previous) => { + if (previous.length > 0) { + return previous; + } + return createDefaultWindows(initialSlo); + }); }, [initialSlo]); const onSelectedSlo = (slo: SLOResponse | undefined) => { setSelectedSlo(slo); - setRuleParams('sloId', slo?.id); - }; - - const [windowDefs, setWindowDefs] = useState( - ruleParams?.windows || [ - createNewWindow(selectedSlo, { - burnRateThreshold: 14.4, - longWindow: { value: 1, unit: 'h' }, - shortWindow: { value: 5, unit: 'm' }, - actionGroup: ALERT_ACTION.id, - }), - createNewWindow(selectedSlo, { - burnRateThreshold: 6, - longWindow: { value: 6, unit: 'h' }, - shortWindow: { value: 30, unit: 'm' }, - actionGroup: HIGH_PRIORITY_ACTION.id, - }), - createNewWindow(selectedSlo, { - burnRateThreshold: 3, - longWindow: { value: 24, unit: 'h' }, - shortWindow: { value: 120, unit: 'm' }, - actionGroup: MEDIUM_PRIORITY_ACTION.id, - }), - createNewWindow(selectedSlo, { - burnRateThreshold: 1, - longWindow: { value: 72, unit: 'h' }, - shortWindow: { value: 360, unit: 'm' }, - actionGroup: LOW_PRIORITY_ACTION.id, - }), - ] - ); - - // When the SLO changes, recalculate the max burn rates - useEffect(() => { setWindowDefs(() => { - const burnRateDefaults = selectedSlo - ? BURN_RATE_DEFAULTS[selectedSlo?.timeWindow.duration] - : BURN_RATE_DEFAULTS['30d']; - return burnRateDefaults.map((partialWindow) => createNewWindow(selectedSlo, partialWindow)); + return createDefaultWindows(slo); }); - }, [selectedSlo]); + setRuleParams('sloId', slo?.id); + }; useEffect(() => { setRuleParams('windows', windowDefs); @@ -131,3 +96,8 @@ export function BurnRateRuleEditor(props: Props) { ); } + +function createDefaultWindows(slo: SLOResponse | undefined) { + const burnRateDefaults = slo ? BURN_RATE_DEFAULTS[slo.timeWindow.duration] : []; + return burnRateDefaults.map((partialWindow) => createNewWindow(slo, partialWindow)); +} diff --git a/x-pack/plugins/observability/public/components/burn_rate_rule_editor/windows.tsx b/x-pack/plugins/observability/public/components/burn_rate_rule_editor/windows.tsx index 125361940c1fe..69e05a4537d75 100644 --- a/x-pack/plugins/observability/public/components/burn_rate_rule_editor/windows.tsx +++ b/x-pack/plugins/observability/public/components/burn_rate_rule_editor/windows.tsx @@ -17,7 +17,7 @@ import { EuiTitle, EuiSwitch, } from '@elastic/eui'; -import { SLOResponse } from '@kbn/slo-schema'; +import { CreateSLOInput, SLOResponse } from '@kbn/slo-schema'; import { i18n } from '@kbn/i18n'; import numeral from '@elastic/numeral'; import { v4 } from 'uuid'; @@ -51,7 +51,10 @@ const ACTION_GROUP_OPTIONS = [ { value: LOW_PRIORITY_ACTION.id, text: LOW_PRIORITY_ACTION.name }, ]; -export const calculateMaxBurnRateThreshold = (longWindow: Duration, slo?: SLOResponse) => { +export const calculateMaxBurnRateThreshold = ( + longWindow: Duration, + slo?: SLOResponse | CreateSLOInput +) => { return slo ? Math.floor(toMinutes(toDuration(slo.timeWindow.duration)) / toMinutes(longWindow)) : Infinity; @@ -244,7 +247,7 @@ const getErrorBudgetExhaustionText = ( }); export const createNewWindow = ( - slo?: SLOResponse, + slo?: SLOResponse | CreateSLOInput, partialWindow: Partial = {} ): WindowSchema => { const longWindow = partialWindow.longWindow || { value: 1, unit: 'h' }; diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx index 80d7feb20a4e6..ab100bf98bd48 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx @@ -86,7 +86,7 @@ describe('AlertDetailsAppSection', () => { it('should render rule and alert data', async () => { const result = renderComponent(); - expect((await result.findByTestId('thresholdAlertOverviewSection')).children.length).toBe(3); + expect((await result.findByTestId('thresholdAlertOverviewSection')).children.length).toBe(6); expect(result.getByTestId('thresholdRule-2000-2500')).toBeTruthy(); }); @@ -148,7 +148,30 @@ describe('AlertDetailsAppSection', () => { { ['kibana.alert.end']: '2023-03-28T14:40:00.000Z' } ); - expect(alertDetailsAppSectionComponent.getAllByTestId('RuleConditionChart').length).toBe(3); + expect(alertDetailsAppSectionComponent.getAllByTestId('RuleConditionChart').length).toBe(6); expect(mockedRuleConditionChart.mock.calls[0]).toMatchSnapshot(); }); + + it('should render title on condition charts', async () => { + const result = renderComponent(); + + expect(result.getByTestId('chartTitle-0').textContent).toBe( + 'Equation result for count (all documents)' + ); + expect(result.getByTestId('chartTitle-1').textContent).toBe( + 'Equation result for max (system.cpu.user.pct)' + ); + expect(result.getByTestId('chartTitle-2').textContent).toBe( + 'Equation result for min (system.memory.used.pct)' + ); + expect(result.getByTestId('chartTitle-3').textContent).toBe( + 'Equation result for min (system.memory.used.pct) + min (system.memory.used.pct) + min (system.memory.used.pct) + min (system.memory.used.pct...' + ); + expect(result.getByTestId('chartTitle-4').textContent).toBe( + 'Equation result for min (system.memory.used.pct) + min (system.memory.used.pct)' + ); + expect(result.getByTestId('chartTitle-5').textContent).toBe( + 'Equation result for min (system.memory.used.pct) + min (system.memory.used.pct) + min (system.memory.used.pct)' + ); + }); }); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx index 9d2aebddb43ee..2506516efd81a 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx @@ -17,6 +17,9 @@ import { EuiSpacer, EuiText, EuiTitle, + EuiToolTip, + useEuiTheme, + transparentize, } from '@elastic/eui'; import { Rule, RuleTypeParams } from '@kbn/alerting-plugin/common'; import { getPaddedAlertTimeRange } from '@kbn/observability-get-padded-alert-time-range-util'; @@ -35,7 +38,6 @@ import type { RangeEventAnnotationConfig, } from '@kbn/event-annotation-common'; import moment from 'moment'; -import { transparentize, useEuiTheme } from '@elastic/eui'; import { useLicense } from '../../../../hooks/use_license'; import { useKibana } from '../../../../utils/kibana_react'; import { metricValueFormatter } from '../../../../../common/custom_threshold_rule/metric_value_formatter'; @@ -44,6 +46,7 @@ import { AlertParams, CustomThresholdAlertFields, CustomThresholdRuleTypeParams, + MetricExpression, } from '../../types'; import { TIME_LABELS } from '../criterion_preview_chart/criterion_preview_chart'; import { Threshold } from '../custom_threshold'; @@ -64,6 +67,44 @@ interface AppSectionProps { setAlertSummaryFields: React.Dispatch>; } +const CHART_TITLE_LIMIT = 120; + +const equationResultText = i18n.translate('xpack.observability.customThreshold.alertChartTitle', { + defaultMessage: 'Equation result for ', +}); + +const generateChartTitleAndTooltip = (criterion: MetricExpression) => { + const metricNameResolver: Record = {}; + + criterion.metrics.forEach( + (metric) => + (metricNameResolver[metric.name] = `${metric.aggType} (${ + metric.field ? metric.field : metric.filter ? metric.filter : 'all documents' + })`) + ); + + let equation = criterion.equation + ? criterion.equation + : criterion.metrics.map((m) => m.name).join(' + '); + + Object.keys(metricNameResolver) + .sort() + .reverse() + .forEach((metricName) => { + equation = equation.replaceAll(metricName, metricNameResolver[metricName]); + }); + + const chartTitle = + equation.length > CHART_TITLE_LIMIT + ? `${equation.substring(0, CHART_TITLE_LIMIT)}...` + : equation; + + return { + tooltip: `${equationResultText}${equation}`, + title: `${equationResultText}${chartTitle}`, + }; +}; + // eslint-disable-next-line import/no-default-export export default function AlertDetailsAppSection({ alert, @@ -89,6 +130,12 @@ export default function AlertDetailsAppSection({ const groups = alert.fields[ALERT_GROUP]; const tags = alert.fields[TAGS]; + const chartTitleAndTooltip: Array<{ title: string; tooltip: string }> = []; + + ruleParams.criteria.forEach((criterion) => { + chartTitleAndTooltip.push(generateChartTitleAndTooltip(criterion)); + }); + const alertStartAnnotation: PointInTimeEventAnnotationConfig = { label: 'Alert', type: 'manual', @@ -182,9 +229,11 @@ export default function AlertDetailsAppSection({ {ruleParams.criteria.map((criterion, index) => ( - -

{criterion.label || 'CUSTOM'}

-
+ + +

{chartTitleAndTooltip[index].title}

+
+
= (args) => const validationObject = validateCustomThreshold({ criteria: [expression as CustomMetricExpressionParams], searchConfiguration: {}, + uiSettings: {} as IUiSettingsClient, }); setErrors(validationObject.errors[0]); }, [expression]); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/expression_row.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/expression_row.tsx index 6b0643791596a..9612b37e2d10f 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/expression_row.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/expression_row.tsx @@ -307,4 +307,31 @@ export const aggregationType: { [key: string]: AggregationType } = { value: Aggregators.SUM, validNormalizedTypes: ['number', 'histogram'], }, + p95: { + text: i18n.translate( + 'xpack.observability.customThreshold.rule.alertFlyout.aggregationText.p95', + { defaultMessage: '95th Percentile' } + ), + fieldRequired: false, + value: Aggregators.P95, + validNormalizedTypes: ['number', 'histogram'], + }, + p99: { + text: i18n.translate( + 'xpack.observability.customThreshold.rule.alertFlyout.aggregationText.p99', + { defaultMessage: '99th Percentile' } + ), + fieldRequired: false, + value: Aggregators.P99, + validNormalizedTypes: ['number', 'histogram'], + }, + rate: { + text: i18n.translate( + 'xpack.observability..customThreshold.rule.alertFlyout.aggregationText.rate', + { defaultMessage: 'Rate' } + ), + fieldRequired: false, + value: Aggregators.RATE, + validNormalizedTypes: ['number'], + }, }; diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/helpers.test.ts b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/helpers.test.ts new file mode 100644 index 0000000000000..4211907b5d4a0 --- /dev/null +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/helpers.test.ts @@ -0,0 +1,138 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { + Aggregators, + CustomThresholdExpressionMetric, +} from '../../../../../common/custom_threshold_rule/types'; +import { getBufferThreshold, getLensOperationFromRuleMetric, lensFieldFormatter } from './helpers'; +const useCases = [ + [ + { + aggType: Aggregators.SUM, + field: 'system.cpu.user.pct', + filter: '', + name: '', + }, + 'sum(system.cpu.user.pct)', + ], + [ + { + aggType: Aggregators.MAX, + field: 'system.cpu.user.pct', + filter: '', + name: '', + }, + 'max(system.cpu.user.pct)', + ], + [ + { + aggType: Aggregators.MIN, + field: 'system.cpu.user.pct', + filter: '', + name: '', + }, + 'min(system.cpu.user.pct)', + ], + [ + { + aggType: Aggregators.AVERAGE, + field: 'system.cpu.user.pct', + filter: '', + name: '', + }, + 'average(system.cpu.user.pct)', + ], + [ + { + aggType: Aggregators.COUNT, + field: 'system.cpu.user.pct', + filter: '', + name: '', + }, + 'count(___records___)', + ], + [ + { + aggType: Aggregators.CARDINALITY, + field: 'system.cpu.user.pct', + filter: '', + name: '', + }, + 'unique_count(system.cpu.user.pct)', + ], + [ + { + aggType: Aggregators.P95, + field: 'system.cpu.user.pct', + filter: '', + name: '', + }, + 'percentile(system.cpu.user.pct, percentile=95)', + ], + [ + { + aggType: Aggregators.P99, + field: 'system.cpu.user.pct', + filter: '', + name: '', + }, + 'percentile(system.cpu.user.pct, percentile=99)', + ], + [ + { + aggType: Aggregators.RATE, + field: 'system.network.in.bytes', + filter: '', + name: '', + }, + `counter_rate(max(system.network.in.bytes), kql='')`, + ], + [ + { + aggType: Aggregators.RATE, + field: 'system.network.in.bytes', + filter: 'host.name : "foo"', + name: '', + }, + `counter_rate(max(system.network.in.bytes), kql='host.name : foo')`, + ], +]; + +test.each(useCases)('returns the correct operation from %p. => %p', (metric, expectedValue) => { + return expect(getLensOperationFromRuleMetric(metric as CustomThresholdExpressionMetric)).toEqual( + expectedValue + ); +}); + +describe('getBufferThreshold', () => { + const testData = [ + { threshold: undefined, buffer: '0.00' }, + { threshold: 0.1, buffer: '0.12' }, + { threshold: 0.01, buffer: '0.02' }, + { threshold: 0.001, buffer: '0.01' }, + { threshold: 0.00098, buffer: '0.01' }, + { threshold: 130, buffer: '143.00' }, + ]; + + it.each(testData)('getBufferThreshold($threshold) = $buffer', ({ threshold, buffer }) => { + expect(getBufferThreshold(threshold)).toBe(buffer); + }); +}); + +describe('lensFieldFormatter', () => { + const testData = [ + { metrics: [{ field: 'system.bytes' }], format: 'bits' }, + { metrics: [{ field: 'system.pct' }], format: 'percent' }, + { metrics: [{ field: 'system.host.cores' }], format: 'number' }, + { metrics: [{ field: undefined }], format: 'number' }, + ]; + it.each(testData)('getBufferThreshold($threshold) = $buffer', ({ metrics, format }) => { + expect(lensFieldFormatter(metrics as unknown as CustomThresholdExpressionMetric[])).toBe( + format + ); + }); +}); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/helpers.ts b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/helpers.ts new file mode 100644 index 0000000000000..1875af6ceb93e --- /dev/null +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/helpers.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + Aggregators, + CustomThresholdExpressionMetric, +} from '../../../../../common/custom_threshold_rule/types'; + +export const getLensOperationFromRuleMetric = (metric: CustomThresholdExpressionMetric): string => { + const { aggType, field, filter } = metric; + let operation: string = aggType; + const operationArgs: string[] = []; + const aggFilter = JSON.stringify(filter || '').replace(/"|\\/g, ''); + + if (aggType === Aggregators.RATE) { + return `counter_rate(max(${field}), kql='${aggFilter}')`; + } + + if (aggType === Aggregators.AVERAGE) operation = 'average'; + if (aggType === Aggregators.CARDINALITY) operation = 'unique_count'; + if (aggType === Aggregators.P95 || aggType === Aggregators.P99) operation = 'percentile'; + if (aggType === Aggregators.COUNT) operation = 'count'; + + let sourceField = field; + + if (aggType === Aggregators.COUNT) { + sourceField = '___records___'; + } + + operationArgs.push(sourceField || ''); + + if (aggType === Aggregators.P95) { + operationArgs.push('percentile=95'); + } + + if (aggType === Aggregators.P99) { + operationArgs.push('percentile=99'); + } + + if (aggFilter) operationArgs.push(`kql='${aggFilter}'`); + + return operation + '(' + operationArgs.join(', ') + ')'; +}; + +export const getBufferThreshold = (threshold?: number): string => + (Math.ceil((threshold || 0) * 1.1 * 100) / 100).toFixed(2).toString(); + +export const LensFieldFormat = { + NUMBER: 'number', + PERCENT: 'percent', + BITS: 'bits', +} as const; + +export const lensFieldFormatter = ( + metrics: CustomThresholdExpressionMetric[] +): typeof LensFieldFormat[keyof typeof LensFieldFormat] => { + if (metrics.length < 1 || !metrics[0].field) return LensFieldFormat.NUMBER; + const firstMetricField = metrics[0].field; + if (firstMetricField.endsWith('.pct')) return LensFieldFormat.PERCENT; + if (firstMetricField.endsWith('.bytes')) return LensFieldFormat.BITS; + return LensFieldFormat.NUMBER; +}; + +export const isRate = (metrics: CustomThresholdExpressionMetric[]): boolean => + Boolean(metrics.length > 0 && metrics[0].aggType === Aggregators.RATE); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.test.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.test.tsx index 320958a20c0e8..7aab6dd0d636b 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.test.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.test.tsx @@ -13,7 +13,7 @@ import { Comparator, Aggregators } from '../../../../../common/custom_threshold_ import { useKibana } from '../../../../utils/kibana_react'; import { kibanaStartMock } from '../../../../utils/kibana_react.mock'; import { MetricExpression } from '../../types'; -import { getBufferThreshold, RuleConditionChart } from './rule_condition_chart'; +import { RuleConditionChart } from './rule_condition_chart'; jest.mock('../../../../utils/kibana_react'); @@ -71,18 +71,3 @@ describe('Rule condition chart', () => { expect(wrapper.find('[data-test-subj="thresholdRuleNoChartData"]').exists()).toBeTruthy(); }); }); - -describe('getBufferThreshold', () => { - const testData = [ - { threshold: undefined, buffer: '0.00' }, - { threshold: 0.1, buffer: '0.12' }, - { threshold: 0.01, buffer: '0.02' }, - { threshold: 0.001, buffer: '0.01' }, - { threshold: 0.00098, buffer: '0.01' }, - { threshold: 130, buffer: '143.00' }, - ]; - - it.each(testData)('getBufferThreshold($threshold) = $buffer', ({ threshold, buffer }) => { - expect(getBufferThreshold(threshold)).toBe(buffer); - }); -}); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx index bc2701eb79489..e1eefcfc2f706 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx @@ -6,7 +6,7 @@ */ import React, { useState, useEffect } from 'react'; import { EuiEmptyPrompt, useEuiTheme } from '@elastic/eui'; -import { FillStyle, OperationType, SeriesType } from '@kbn/lens-plugin/public'; +import { FillStyle, SeriesType } from '@kbn/lens-plugin/public'; import { DataView } from '@kbn/data-views-plugin/common'; import { FormattedMessage } from '@kbn/i18n-react'; import useAsync from 'react-use/lib/useAsync'; @@ -19,19 +19,22 @@ import { XYReferenceLinesLayer, XYByValueAnnotationsLayer, } from '@kbn/lens-embeddable-utils'; - import { IErrorObject } from '@kbn/triggers-actions-ui-plugin/public'; import { i18n } from '@kbn/i18n'; import { TimeRange } from '@kbn/es-query'; import { EventAnnotationConfig } from '@kbn/event-annotation-common'; -import { - Aggregators, - Comparator, - AggType, -} from '../../../../../common/custom_threshold_rule/types'; +import { EventsAsUnit } from '../../../../../common/constants'; +import { Comparator } from '../../../../../common/custom_threshold_rule/types'; import { useKibana } from '../../../../utils/kibana_react'; import { MetricExpression } from '../../types'; import { AggMap, PainlessTinyMathParser } from './painless_tinymath_parser'; +import { + lensFieldFormatter, + getBufferThreshold, + getLensOperationFromRuleMetric, + isRate, + LensFieldFormat, +} from './helpers'; interface RuleConditionChartProps { metricExpression: MetricExpression; @@ -44,15 +47,6 @@ interface RuleConditionChartProps { seriesType?: SeriesType; } -const getOperationTypeFromRuleAggType = (aggType: AggType): OperationType => { - if (aggType === Aggregators.AVERAGE) return 'average'; - if (aggType === Aggregators.CARDINALITY) return 'unique_count'; - return aggType; -}; - -export const getBufferThreshold = (threshold?: number): string => - (Math.ceil((threshold || 0) * 1.1 * 100) / 100).toFixed(2).toString(); - export function RuleConditionChart({ metricExpression, dataView, @@ -107,13 +101,6 @@ export function RuleConditionChart({ useEffect(() => { if (!threshold) return; const refLayers = []; - const isPercent = Boolean(metrics.length === 1 && metrics[0].field?.endsWith('.pct')); - const format = { - id: isPercent ? 'percent' : 'number', - params: { - decimals: isPercent ? 0 : 2, - }, - }; if ( comparator === Comparator.OUTSIDE_RANGE || @@ -125,7 +112,6 @@ export function RuleConditionChart({ value: (threshold[0] || 0).toString(), color: euiTheme.colors.danger, fill: comparator === Comparator.OUTSIDE_RANGE ? 'below' : 'none', - format, }, ], }); @@ -135,7 +121,6 @@ export function RuleConditionChart({ value: (threshold[1] || 0).toString(), color: euiTheme.colors.danger, fill: comparator === Comparator.OUTSIDE_RANGE ? 'above' : 'none', - format, }, ], }); @@ -152,7 +137,6 @@ export function RuleConditionChart({ value: (threshold[0] || 0).toString(), color: euiTheme.colors.danger, fill, - format, }, ], }); @@ -163,7 +147,6 @@ export function RuleConditionChart({ value: getBufferThreshold(threshold[0]), color: 'transparent', fill, - format, }, ], }); @@ -191,17 +174,7 @@ export function RuleConditionChart({ return; } const aggMapFromMetrics = metrics.reduce((acc, metric) => { - const operation = getOperationTypeFromRuleAggType(metric.aggType); - let sourceField = metric.field; - - if (metric.aggType === Aggregators.COUNT) { - sourceField = '___records___'; - } - let operationField = `${operation}(${sourceField})`; - if (metric?.filter) { - const aggFilter = JSON.stringify(metric.filter).replace(/"|\\/g, ''); - operationField = `${operation}(${sourceField},kql='${aggFilter}')`; - } + const operationField = getLensOperationFromRuleMetric(metric); return { ...acc, [metric.name]: operationField, @@ -232,16 +205,17 @@ export function RuleConditionChart({ if (!formulaAsync.value || !dataView || !formula) { return; } - const isPercent = Boolean(metrics.length === 1 && metrics[0].field?.endsWith('.pct')); + const formatId = lensFieldFormatter(metrics); const baseLayer = { type: 'formula', value: formula, label: 'Custom Threshold', groupBy, format: { - id: isPercent ? 'percent' : 'number', + id: formatId, params: { - decimals: isPercent ? 0 : 2, + decimals: formatId === LensFieldFormat.PERCENT ? 0 : 2, + suffix: isRate(metrics) && formatId === LensFieldFormat.NUMBER ? EventsAsUnit : undefined, }, }, }; @@ -273,6 +247,8 @@ export function RuleConditionChart({ value: layer.value, label: layer.label, format: layer.format, + // We always scale the chart with seconds with RATE Agg. + timeScale: isRate(metrics) ? 's' : undefined, })), options: xYDataLayerOptions, }); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/validation.test.ts b/x-pack/plugins/observability/public/components/custom_threshold/components/validation.test.ts index ac1b545e4a302..f1c5bc9576763 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/validation.test.ts +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/validation.test.ts @@ -5,7 +5,25 @@ * 2.0. */ -import { EQUATION_REGEX } from './validation'; +import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; +import { + CustomMetricExpressionParams, + CustomThresholdExpressionMetric, +} from '../../../../common/custom_threshold_rule/types'; +import { EQUATION_REGEX, validateCustomThreshold } from './validation'; + +const errorReason = 'this should appear as error reason'; + +jest.mock('@kbn/es-query', () => { + const actual = jest.requireActual('@kbn/es-query'); + return { + ...actual, + buildEsQuery: jest.fn(() => { + // eslint-disable-next-line no-throw-literal + throw { shortMessage: errorReason }; + }), + }; +}); describe('Metric Threshold Validation', () => { describe('valid equations', () => { @@ -30,4 +48,46 @@ describe('Metric Threshold Validation', () => { }); }); }); + it('should throw an error when data view is not provided', () => { + const res = validateCustomThreshold({ + uiSettings: {} as IUiSettingsClient, + searchConfiguration: {}, + criteria: { + metrics: [ + { + name: 'Test', + aggType: 'count', + field: 'system.cpu.cores', + filter: 'none valid filter', + }, + ] as unknown as CustomThresholdExpressionMetric[], + } as unknown as CustomMetricExpressionParams[], + }); + expect(res.errors.searchConfiguration[0]).toBe('Data view is required.'); + }); + it('should throw an error when filter query is not valid with reason', () => { + const res = validateCustomThreshold({ + uiSettings: { + get: jest.fn(), + } as unknown as IUiSettingsClient, + searchConfiguration: { + index: 'test*', + query: { + language: `kuery`, + query: 'test:tet', + }, + }, + criteria: { + metrics: [ + { + name: 'Test', + aggType: 'count', + field: 'system.cpu.cores', + filter: 'none valid filter', + }, + ] as unknown as CustomThresholdExpressionMetric[], + } as unknown as CustomMetricExpressionParams[], + }); + expect(res.errors.filterQuery[0]).toBe(`Filter query is invalid. ${errorReason}`); + }); }); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/validation.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/validation.tsx index 2144757216fbe..eb7e86c090dc2 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/validation.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/validation.tsx @@ -5,7 +5,8 @@ * 2.0. */ -import { Query, SerializedSearchSourceFields } from '@kbn/data-plugin/common'; +import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; +import { getEsQueryConfig, Query, SerializedSearchSourceFields } from '@kbn/data-plugin/common'; import { buildEsQuery, fromKueryExpression } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import { ValidationResult } from '@kbn/triggers-actions-ui-plugin/public'; @@ -20,9 +21,11 @@ export const EQUATION_REGEX = /[^A-Z|+|\-|\s|\d+|\.|\(|\)|\/|\*|>|<|=|\?|\:|&|\! export function validateCustomThreshold({ criteria, searchConfiguration, + uiSettings, }: { criteria: CustomMetricExpressionParams[]; searchConfiguration: SerializedSearchSourceFields; + uiSettings: IUiSettingsClient; }): ValidationResult { const validationResult = { errors: {} }; const errors: { @@ -56,14 +59,17 @@ export function validateCustomThreshold({ buildEsQuery( undefined, [{ query: (searchConfiguration.query as Query).query, language: 'kuery' }], - [] + [], + getEsQueryConfig(uiSettings) ); } catch (e) { + const errorReason = e.shortMessage || ''; errors.filterQuery = [ i18n.translate( 'xpack.observability.customThreshold.rule.alertFlyout.error.invalidFilterQuery', { - defaultMessage: 'Filter query is invalid.', + values: { errorReason }, + defaultMessage: `Filter query is invalid. {errorReason}`, } ), ]; diff --git a/x-pack/plugins/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts b/x-pack/plugins/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts index 124003879e53e..7616b1b9e2953 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts +++ b/x-pack/plugins/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts @@ -103,6 +103,62 @@ export const buildCustomThresholdRule = ( timeSize: 15, timeUnit: 'm', }, + { + comparator: Comparator.GT, + metrics: [ + { + name: 'A', + aggType: Aggregators.MIN, + field: 'system.memory.used.pct', + }, + ], + threshold: [0.8], + timeSize: 15, + timeUnit: 'm', + equation: + 'A + A + A + A + A + A + A + A + A + A + A + A + A + A + A + A + A + A + A + A + A', + }, + { + comparator: Comparator.GT, + metrics: [ + { + name: 'C', + aggType: Aggregators.MIN, + field: 'system.memory.used.pct', + }, + { + name: 'D', + aggType: Aggregators.MIN, + field: 'system.memory.used.pct', + }, + ], + threshold: [0.8], + timeSize: 15, + timeUnit: 'm', + }, + { + comparator: Comparator.GT, + metrics: [ + { + name: 'CAD', + aggType: Aggregators.MIN, + field: 'system.memory.used.pct', + }, + { + name: 'CADE', + aggType: Aggregators.MIN, + field: 'system.memory.used.pct', + }, + { + name: 'ADE', + aggType: Aggregators.MIN, + field: 'system.memory.used.pct', + }, + ], + threshold: [0.8], + timeSize: 15, + timeUnit: 'm', + }, ], searchConfiguration: { query: { diff --git a/x-pack/plugins/observability/public/hooks/use_create_rule.ts b/x-pack/plugins/observability/public/hooks/use_create_rule.ts new file mode 100644 index 0000000000000..7c30544105aaa --- /dev/null +++ b/x-pack/plugins/observability/public/hooks/use_create_rule.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useMutation } from '@tanstack/react-query'; +import { i18n } from '@kbn/i18n'; +import { BASE_ALERTING_API_PATH, RuleTypeParams } from '@kbn/alerting-plugin/common'; +import { v4 } from 'uuid'; +import { + CreateRuleRequestBody, + CreateRuleResponse, +} from '@kbn/alerting-plugin/common/routes/rule/apis/create'; +import { useKibana } from '../utils/kibana_react'; + +export function useCreateRule() { + const { + http, + notifications: { toasts }, + } = useKibana().services; + + const createRule = useMutation< + CreateRuleResponse, + Error, + { rule: CreateRuleRequestBody } + >( + ['createRule'], + ({ rule }) => { + try { + const ruleId = v4(); + const body = JSON.stringify(rule); + return http.post(`${BASE_ALERTING_API_PATH}/rule/${ruleId}`, { + body, + }); + } catch (e) { + throw new Error(`Unable to create rule: ${e}`); + } + }, + { + onError: (_err) => { + toasts.addDanger( + i18n.translate('xpack.observability.rules.createRule.errorNotification.descriptionText', { + defaultMessage: 'Failed to create rule', + }) + ); + }, + + onSuccess: () => { + toasts.addSuccess( + i18n.translate( + 'xpack.observability.rules.createRule.successNotification.descriptionText', + { + defaultMessage: 'Rule created', + } + ) + ); + }, + } + ); + + return createRule; +} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx index bf32535118293..dfc5fb1a6f563 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx @@ -5,25 +5,15 @@ * 2.0. */ -import { - EuiButton, - EuiButtonEmpty, - EuiCheckbox, - EuiFlexGroup, - EuiIconTip, - EuiSpacer, - EuiSteps, -} from '@elastic/eui'; +import { EuiButton, EuiButtonEmpty, EuiFlexGroup, EuiSpacer, EuiSteps } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import type { GetSLOResponse } from '@kbn/slo-schema'; -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; import { InspectSLOPortal } from './common/inspect_slo_portal'; import { EquivalentApiRequest } from './common/equivalent_api_request'; -import { BurnRateRuleFlyout } from '../../slos/components/common/burn_rate_rule_flyout'; import { paths } from '../../../../common/locators/paths'; import { useCreateSlo } from '../../../hooks/slo/use_create_slo'; -import { useFetchRulesForSlo } from '../../../hooks/slo/use_fetch_rules_for_slo'; import { useUpdateSlo } from '../../../hooks/slo/use_update_slo'; import { useKibana } from '../../../utils/kibana_react'; import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../constants'; @@ -32,10 +22,6 @@ import { transformSloResponseToCreateSloForm, transformValuesToUpdateSLOInput, } from '../helpers/process_slo_form_values'; -import { - CREATE_RULE_SEARCH_PARAM, - useAddRuleFlyoutState, -} from '../hooks/use_add_rule_flyout_state'; import { useParseUrlState } from '../hooks/use_parse_url_state'; import { useSectionFormValidation } from '../hooks/use_section_form_validation'; import { useShowSections } from '../hooks/use_show_sections'; @@ -43,6 +29,9 @@ import { CreateSLOForm } from '../types'; import { SloEditFormDescriptionSection } from './slo_edit_form_description_section'; import { SloEditFormIndicatorSection } from './slo_edit_form_indicator_section'; import { SloEditFormObjectiveSection } from './slo_edit_form_objective_section'; +import { useCreateRule } from '../../../hooks/use_create_rule'; +import { createBurnRateRuleRequestBody } from '../helpers/create_burn_rate_rule_request_body'; +import { BurnRateRuleParams } from '../../../typings'; export interface Props { slo?: GetSLOResponse; @@ -57,22 +46,9 @@ export function SloEditForm({ slo }: Props) { } = useKibana().services; const isEditMode = slo !== undefined; - const { data: rules, isInitialLoading } = useFetchRulesForSlo({ - sloIds: slo?.id ? [slo.id] : undefined, - }); - const sloFormValuesFromUrlState = useParseUrlState(); const sloFormValuesFromSloResponse = transformSloResponseToCreateSloForm(slo); - const isAddRuleFlyoutOpen = useAddRuleFlyoutState(isEditMode); - const [isCreateRuleCheckboxChecked, setIsCreateRuleCheckboxChecked] = useState(true); - - useEffect(() => { - if (isEditMode && rules && rules[slo.id].length) { - setIsCreateRuleCheckboxChecked(false); - } - }, [isEditMode, rules, slo]); - const methods = useForm({ defaultValues: SLO_EDIT_FORM_DEFAULT_VALUES, values: sloFormValuesFromUrlState ? sloFormValuesFromUrlState : sloFormValuesFromSloResponse, @@ -97,6 +73,8 @@ export function SloEditForm({ slo }: Props) { const { mutateAsync: createSlo, isLoading: isCreateSloLoading } = useCreateSlo(); const { mutateAsync: updateSlo, isLoading: isUpdateSloLoading } = useUpdateSlo(); + const { mutateAsync: createBurnRateRule, isLoading: isCreateBurnRateRuleLoading } = + useCreateRule(); const handleSubmit = async () => { const isValid = await trigger(); @@ -108,30 +86,15 @@ export function SloEditForm({ slo }: Props) { if (isEditMode) { const processedValues = transformValuesToUpdateSLOInput(values); - - if (isCreateRuleCheckboxChecked) { - await updateSlo({ sloId: slo.id, slo: processedValues }); - navigate( - basePath.prepend( - `${paths.observability.sloEdit(slo.id)}?${CREATE_RULE_SEARCH_PARAM}=true` - ) - ); - } else { - updateSlo({ sloId: slo.id, slo: processedValues }); - navigate(basePath.prepend(paths.observability.slos)); - } + updateSlo({ sloId: slo.id, slo: processedValues }); + navigate(basePath.prepend(paths.observability.slos)); } else { const processedValues = transformCreateSLOFormToCreateSLOInput(values); - - if (isCreateRuleCheckboxChecked) { - const { id } = await createSlo({ slo: processedValues }); - navigate( - basePath.prepend(`${paths.observability.sloEdit(id)}?${CREATE_RULE_SEARCH_PARAM}=true`) - ); - } else { - createSlo({ slo: processedValues }); - navigate(basePath.prepend(paths.observability.slos)); - } + const resp = await createSlo({ slo: processedValues }); + await createBurnRateRule({ + rule: createBurnRateRuleRequestBody({ ...processedValues, id: resp.id }), + }); + navigate(basePath.prepend(paths.observability.slos)); } }; @@ -140,10 +103,6 @@ export function SloEditForm({ slo }: Props) { [navigateToUrl] ); - const handleChangeCheckbox = () => { - setIsCreateRuleCheckboxChecked(!isCreateRuleCheckboxChecked); - }; - return ( <> @@ -175,36 +134,6 @@ export function SloEditForm({ slo }: Props) { ]} /> - - - - {i18n.translate('xpack.observability.slo.sloEdit.createAlert.title', { - defaultMessage: 'Create an', - })}{' '} - - {i18n.translate('xpack.observability.slo.sloEdit.createAlert.ruleName', { - defaultMessage: 'SLO burn rate alert rule', - })} - - - - - } - onChange={handleChangeCheckbox} - /> - - @@ -212,7 +141,7 @@ export function SloEditForm({ slo }: Props) { color="primary" data-test-subj="sloFormSubmitButton" fill - isLoading={isCreateSloLoading || isUpdateSloLoading} + isLoading={isCreateSloLoading || isUpdateSloLoading || isCreateBurnRateRuleLoading} onClick={handleSubmit} > {isEditMode @@ -243,12 +172,6 @@ export function SloEditForm({ slo }: Props) { - - ); } diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/metric_indicator.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/metric_indicator.tsx index b8105814b852e..d3098208fa6c1 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/metric_indicator.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/metric_indicator.tsx @@ -270,10 +270,11 @@ export function MetricIndicator({ indexFields, isLoadingIndex }: MetricIndicator defaultValue={0} render={({ field: { ref, ...field }, fieldState }) => ( field.onChange(Number(event.target.value))} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/helpers/create_burn_rate_rule_request_body.ts b/x-pack/plugins/observability/public/pages/slo_edit/helpers/create_burn_rate_rule_request_body.ts new file mode 100644 index 0000000000000..0128d6c7d6360 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/helpers/create_burn_rate_rule_request_body.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CreateSLOInput } from '@kbn/slo-schema'; +import { i18n } from '@kbn/i18n'; +import { CreateRuleRequestBody } from '@kbn/alerting-plugin/common/routes/rule/apis/create'; +import { BURN_RATE_DEFAULTS } from '../../../components/burn_rate_rule_editor/constants'; +import { createNewWindow } from '../../../components/burn_rate_rule_editor/windows'; +import { BurnRateRuleParams } from '../../../typings'; + +function createBurnRateWindowsFromSLO(slo: CreateSLOInput) { + const burnRateDefaults = slo + ? BURN_RATE_DEFAULTS[slo?.timeWindow.duration] + : BURN_RATE_DEFAULTS['30d']; + return burnRateDefaults.map((partialWindow) => createNewWindow(slo, partialWindow)); +} + +export function createBurnRateRuleRequestBody( + slo: CreateSLOInput & { id: string } +): CreateRuleRequestBody { + return { + params: { + sloId: slo.id, + windows: createBurnRateWindowsFromSLO(slo), + }, + consumer: 'slo', + schedule: { interval: '1m' }, + tags: [], + name: i18n.translate('xpack.observability.slo.burnRateRule.name', { + defaultMessage: '{name} Burn Rate rule', + values: { name: slo.name }, + }), + rule_type_id: 'slo.rules.burnRate', + actions: [], + enabled: true, + }; +} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.test.tsx b/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.test.tsx index 9ef10ea1d928a..f843109f53542 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.test.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.test.tsx @@ -20,6 +20,7 @@ import { useCreateSlo } from '../../hooks/slo/use_create_slo'; import { useFetchApmSuggestions } from '../../hooks/slo/use_fetch_apm_suggestions'; import { useFetchSloDetails } from '../../hooks/slo/use_fetch_slo_details'; import { useUpdateSlo } from '../../hooks/slo/use_update_slo'; +import { useCreateRule } from '../../hooks/use_create_rule'; import { useFetchDataViews } from '../../hooks/use_fetch_data_views'; import { useFetchIndices } from '../../hooks/use_fetch_indices'; import { useKibana } from '../../utils/kibana_react'; @@ -39,6 +40,7 @@ jest.mock('../../hooks/use_fetch_data_views'); jest.mock('../../hooks/slo/use_fetch_slo_details'); jest.mock('../../hooks/slo/use_create_slo'); jest.mock('../../hooks/slo/use_update_slo'); +jest.mock('../../hooks/use_create_rule'); jest.mock('../../hooks/slo/use_fetch_apm_suggestions'); jest.mock('../../hooks/slo/use_capabilities'); @@ -54,6 +56,7 @@ const useFetchDataViewsMock = useFetchDataViews as jest.Mock; const useFetchSloMock = useFetchSloDetails as jest.Mock; const useCreateSloMock = useCreateSlo as jest.Mock; const useUpdateSloMock = useUpdateSlo as jest.Mock; +const useCreateRuleMock = useCreateRule as jest.Mock; const useFetchApmSuggestionsMock = useFetchApmSuggestions as jest.Mock; const useCapabilitiesMock = useCapabilities as jest.Mock; @@ -131,8 +134,9 @@ const mockKibana = (license: ILicense | null = licenseMock) => { }; describe('SLO Edit Page', () => { - const mockCreate = jest.fn(); + const mockCreate = jest.fn(() => Promise.resolve({ id: 'mock-slo-id' })); const mockUpdate = jest.fn(); + const mockCreateRule = jest.fn(); const history = createBrowserHistory(); @@ -163,6 +167,13 @@ describe('SLO Edit Page', () => { mutateAsync: mockCreate, }); + useCreateRuleMock.mockReturnValue({ + isLoading: false, + isSuccess: false, + isError: false, + mutateAsync: mockCreateRule, + }); + useUpdateSloMock.mockReturnValue({ isLoading: false, isSuccess: false, @@ -393,7 +404,7 @@ describe('SLO Edit Page', () => { }); describe('when submitting has completed successfully', () => { - it('navigates to the SLO List page when checkbox to create new rule is not checked', async () => { + it('navigates to the SLO List page', async () => { const slo = buildSlo(); jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); @@ -414,52 +425,6 @@ describe('SLO Edit Page', () => { expect(mockNavigate).toBeCalledWith(mockBasePathPrepend(paths.observability.slos)); }); }); - - it('navigates to the SLO Edit page when checkbox to create new rule is checked', async () => { - const slo = buildSlo(); - - jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); - jest - .spyOn(Router, 'useLocation') - .mockReturnValue({ pathname: '/slos/123/edit', search: '', state: '', hash: '' }); - - useFetchSloMock.mockReturnValue({ isLoading: false, data: slo }); - - const { getByTestId } = render(); - - expect(getByTestId('sloFormSubmitButton')).toBeEnabled(); - - await waitFor(() => { - fireEvent.click(getByTestId('createNewRuleCheckbox')); - fireEvent.click(getByTestId('sloFormSubmitButton')); - }); - - await waitFor(() => { - expect(mockNavigate).toBeCalledWith( - mockBasePathPrepend(`${paths.observability.sloEdit(slo.id)}?create-rule=true`) - ); - }); - }); - - it('opens the Add Rule Flyout when visiting an existing SLO with search params set', async () => { - const slo = buildSlo(); - - jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); - jest.spyOn(Router, 'useLocation').mockReturnValue({ - pathname: '/slos/123/edit', - search: 'create-rule=true', - state: '', - hash: '', - }); - - useFetchSloMock.mockReturnValue({ isLoading: false, data: slo }); - - const { getByTestId } = render(); - - await waitFor(() => { - expect(getByTestId('add-rule-flyout')).toBeTruthy(); - }); - }); }); }); }); diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx index 9ff1e3c14a2b2..d801bd848ce9b 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx @@ -10,6 +10,7 @@ import { EuiFlexGroup, EuiSkeletonRectangle } from '@elastic/eui'; import { SLOWithSummaryResponse } from '@kbn/slo-schema'; import { Rule } from '@kbn/triggers-actions-ui-plugin/public'; +import { SloTagsList } from '../common/slo_tags_list'; import { SloIndicatorTypeBadge } from './slo_indicator_type_badge'; import { SloStatusBadge } from '../../../../components/slo/slo_status_badge'; import { SloActiveAlertsBadge } from '../../../../components/slo/slo_status_badge/slo_active_alerts_badge'; @@ -41,11 +42,12 @@ export function SloBadges({ ) : ( <> + - + )} diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_indicator_type_badge.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_indicator_type_badge.tsx index 60fceb4481b91..bf33f22e25221 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_indicator_type_badge.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_indicator_type_badge.tsx @@ -15,6 +15,7 @@ import { } from '@kbn/slo-schema'; import { euiLightVars } from '@kbn/ui-theme'; import React from 'react'; +import { useUrlSearchState } from '../../hooks/use_url_search_state'; import { useKibana } from '../../../../utils/kibana_react'; import { convertSliApmParamsToApmAppDeeplinkUrl } from '../../../../utils/slo/convert_sli_apm_params_to_apm_app_deeplink_url'; import { toIndicatorTypeLabel } from '../../../../utils/slo/labels'; @@ -30,6 +31,8 @@ export function SloIndicatorTypeBadge({ slo, color }: Props) { http: { basePath }, } = useKibana().services; + const { onStateChange } = useUrlSearchState(); + const handleNavigateToApm = () => { const url = convertSliApmParamsToApmAppDeeplinkUrl(slo); if (url) { @@ -40,7 +43,21 @@ export function SloIndicatorTypeBadge({ slo, color }: Props) { return ( <> - + { + onStateChange({ + kqlQuery: `slo.indicator.type: ${slo.indicator.type}`, + }); + }} + onClickAriaLabel={i18n.translate( + 'xpack.observability.slo.indicatorTypeBadge.clickToFilter', + { + defaultMessage: 'Click to filter by {indicatorType} SLOs', + values: { indicatorType: toIndicatorTypeLabel(slo.indicator.type) }, + } + )} + > {toIndicatorTypeLabel(slo.indicator.type)} diff --git a/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item_badges.tsx b/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item_badges.tsx index 1fec95eb98f73..66effe39c48bc 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item_badges.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item_badges.tsx @@ -6,10 +6,12 @@ */ import { SLOWithSummaryResponse } from '@kbn/slo-schema'; -import React from 'react'; +import React, { useCallback } from 'react'; import { Rule } from '@kbn/triggers-actions-ui-plugin/public'; import styled from 'styled-components'; import { EuiFlexGroup } from '@elastic/eui'; +import { SloTagsList } from '../common/slo_tags_list'; +import { useUrlSearchState } from '../../hooks/use_url_search_state'; import { LoadingBadges } from '../badges/slo_badges'; import { SloIndicatorTypeBadge } from '../badges/slo_indicator_type_badge'; import { SloTimeWindowBadge } from '../badges/slo_time_window_badge'; @@ -31,6 +33,16 @@ const Container = styled.div` `; export function SloCardItemBadges({ slo, activeAlerts, rules, handleCreateRule }: Props) { + const { onStateChange } = useUrlSearchState(); + + const onTagClick = useCallback( + (tag: string) => { + onStateChange({ + kqlQuery: `slo.tags: "${tag}"`, + }); + }, + [onStateChange] + ); return ( @@ -42,6 +54,13 @@ export function SloCardItemBadges({ slo, activeAlerts, rules, handleCreateRule } + )} diff --git a/x-pack/plugins/observability/public/pages/slos/components/card_view/slos_card_view.tsx b/x-pack/plugins/observability/public/pages/slos/components/card_view/slos_card_view.tsx index 0829fce91a937..8397a8ce99472 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/card_view/slos_card_view.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/card_view/slos_card_view.tsx @@ -29,14 +29,14 @@ export interface Props { const useColumns = () => { const isMobile = useIsWithinBreakpoints(['xs', 's']); const isMedium = useIsWithinBreakpoints(['m']); - const isLarge = useIsWithinBreakpoints(['l', 'xl']); + const isXLarge = useIsWithinBreakpoints(['xl']); switch (true) { case isMobile: return 1; case isMedium: return 3; - case isLarge: + case isXLarge: return 4; default: return 3; diff --git a/x-pack/plugins/observability/public/pages/slos/components/common/slo_tags_list.tsx b/x-pack/plugins/observability/public/pages/slos/components/common/slo_tags_list.tsx new file mode 100644 index 0000000000000..66a415a8946ad --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slos/components/common/slo_tags_list.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback } from 'react'; +import { TagsList } from '@kbn/observability-shared-plugin/public'; +import type { TagsListProps } from '@kbn/observability-shared-plugin/public'; +import { useUrlSearchState } from '../../hooks/use_url_search_state'; + +export function SloTagsList(props: TagsListProps) { + const { onStateChange } = useUrlSearchState(); + + const onTagClick = useCallback( + (tag: string) => { + onStateChange({ + kqlQuery: `slo.tags: "${tag}"`, + }); + }, + [onStateChange] + ); + + return ; +} diff --git a/x-pack/plugins/observability/public/pages/slos/components/compact_view/slo_list_compact_view.tsx b/x-pack/plugins/observability/public/pages/slos/components/compact_view/slo_list_compact_view.tsx index 177be6f062923..3a07bc3ac08b6 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/compact_view/slo_list_compact_view.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/compact_view/slo_list_compact_view.tsx @@ -19,6 +19,7 @@ import { i18n } from '@kbn/i18n'; import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema'; import { useQueryClient } from '@tanstack/react-query'; import React, { useState } from 'react'; +import { SloTagsList } from '../common/slo_tags_list'; import { useCloneSlo } from '../../../../hooks/slo/use_clone_slo'; import { rulesLocatorID, sloFeatureId } from '../../../../../common'; import { SLO_BURN_RATE_RULE_TYPE_ID } from '../../../../../common/constants'; @@ -233,7 +234,7 @@ export function SloListCompactView({ sloList, loading, error }: Props) { { field: 'name', name: 'Name', - width: '20%', + width: '15%', truncateText: { lines: 2 }, 'data-test-subj': 'sloItem', render: (_, slo: SLOWithSummaryResponse) => { @@ -258,6 +259,11 @@ export function SloListCompactView({ sloList, loading, error }: Props) { ); }, }, + { + field: 'tags', + name: 'Tags', + render: (tags: string[]) => , + }, { field: 'instance', name: 'Instance', @@ -379,6 +385,7 @@ export function SloListCompactView({ sloList, loading, error }: Props) { columns={columns} loading={loading} noItemsMessage={loading ? LOADING_SLOS_LABEL : NO_SLOS_FOUND} + tableLayout="auto" /> {sloToAddRule ? ( { - storeState({ page: pageNumber }); + onStateChange({ page: pageNumber }); }; if (isLoading) { @@ -84,7 +84,7 @@ export function GroupView({ kqlQuery, sloView, sort, direction, groupBy }: Props itemsPerPage={perPage} itemsPerPageOptions={[10, 25, 50, 100]} onChangeItemsPerPage={(newPerPage) => { - storeState({ perPage: newPerPage }); + onStateChange({ perPage: newPerPage }); }} />
diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx index 2f2d466699c1f..19617273659b1 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx @@ -15,7 +15,7 @@ import { ToggleSLOView } from './toggle_slo_view'; import { GroupView } from './grouped_slos/group_view'; export function SloList() { - const { state, store: storeState } = useUrlSearchState(); + const { state, onStateChange: storeState } = useUrlSearchState(); const { view, page, perPage, kqlQuery, filters, tagsFilter, statusFilter, groupBy } = state; const { diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.tsx index 74c47aec2924f..42ece861e3542 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.tsx @@ -30,7 +30,7 @@ export type Item = EuiSelectableOption & { export type ViewMode = 'default' | 'compact'; export function SloListSearchBar() { - const { state, store: storeState } = useUrlSearchState(); + const { state, onStateChange: onChange } = useUrlSearchState(); const { kqlQuery, filters } = state; const containerRef = React.useRef(null); @@ -47,7 +47,7 @@ export function SloListSearchBar() { }); const onStateChange = (newState: Partial) => { - storeState({ page: 0, ...newState }); + onChange({ page: 0, ...newState }); }; const { diff --git a/x-pack/plugins/observability/public/pages/slos/hooks/use_url_search_state.ts b/x-pack/plugins/observability/public/pages/slos/hooks/use_url_search_state.ts index 6bf82aa8db2c8..12154ffd49dd0 100644 --- a/x-pack/plugins/observability/public/pages/slos/hooks/use_url_search_state.ts +++ b/x-pack/plugins/observability/public/pages/slos/hooks/use_url_search_state.ts @@ -46,7 +46,7 @@ export const DEFAULT_STATE = { export function useUrlSearchState(): { state: SearchState; - store: (state: Partial) => Promise; + onStateChange: (state: Partial) => Promise; } { const [state, setState] = useState(DEFAULT_STATE); const history = useHistory(); @@ -77,7 +77,7 @@ export function useUrlSearchState(): { }, [urlStateStorage]); return { state: deepmerge(DEFAULT_STATE, state), - store: (newState: Partial) => + onStateChange: (newState: Partial) => urlStateStorage.current?.set( SLO_LIST_SEARCH_URL_STORAGE_KEY, { ...state, ...newState }, diff --git a/x-pack/plugins/observability/public/pages/slos/slos.test.tsx b/x-pack/plugins/observability/public/pages/slos/slos.test.tsx index 6605eab36b5d7..5b7576fc49a18 100644 --- a/x-pack/plugins/observability/public/pages/slos/slos.test.tsx +++ b/x-pack/plugins/observability/public/pages/slos/slos.test.tsx @@ -21,6 +21,7 @@ import { useDeleteSlo } from '../../hooks/slo/use_delete_slo'; import { useFetchHistoricalSummary } from '../../hooks/slo/use_fetch_historical_summary'; import { useFetchSloList } from '../../hooks/slo/use_fetch_slo_list'; import { useLicense } from '../../hooks/use_license'; +import { TagsList } from '@kbn/observability-shared-plugin/public'; import { useKibana } from '../../utils/kibana_react'; import { render } from '../../utils/test_helper'; import { SlosPage } from './slos'; @@ -46,6 +47,8 @@ const useCreateSloMock = useCreateSlo as jest.Mock; const useDeleteSloMock = useDeleteSlo as jest.Mock; const useFetchHistoricalSummaryMock = useFetchHistoricalSummary as jest.Mock; const useCapabilitiesMock = useCapabilities as jest.Mock; +const TagsListMock = TagsList as jest.Mock; +TagsListMock.mockReturnValue(
Tags list
); const mockCreateSlo = jest.fn(); const mockDeleteSlo = jest.fn(); diff --git a/x-pack/plugins/observability/public/plugin.ts b/x-pack/plugins/observability/public/plugin.ts index d2dcf8c266976..5d6e825d83c43 100644 --- a/x-pack/plugins/observability/public/plugin.ts +++ b/x-pack/plugins/observability/public/plugin.ts @@ -320,7 +320,11 @@ export class Plugin coreSetup.application.register(app); - registerObservabilityRuleTypes(config, this.observabilityRuleTypeRegistry, logsExplorerLocator); + registerObservabilityRuleTypes( + this.observabilityRuleTypeRegistry, + coreSetup.uiSettings, + logsExplorerLocator + ); const assertPlatinumLicense = async () => { const licensing = await pluginsSetup.licensing; diff --git a/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts b/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts index af92943f1cc55..1915778fc2c4b 100644 --- a/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts +++ b/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts @@ -7,7 +7,7 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; -import type { SerializedSearchSourceFields } from '@kbn/data-plugin/common'; +import { SerializedSearchSourceFields } from '@kbn/data-plugin/common'; import { ALERT_REASON, ALERT_RULE_PARAMETERS, @@ -16,11 +16,14 @@ import { } from '@kbn/rule-data-utils'; import type { DiscoverAppLocatorParams } from '@kbn/discover-plugin/common'; import type { LocatorPublic } from '@kbn/share-plugin/common'; +import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import type { MetricExpression } from '../components/custom_threshold/types'; -import type { CustomThresholdExpressionMetric } from '../../common/custom_threshold_rule/types'; +import type { + CustomMetricExpressionParams, + CustomThresholdExpressionMetric, +} from '../../common/custom_threshold_rule/types'; import { getViewInAppUrl } from '../../common/custom_threshold_rule/get_view_in_app_url'; import { SLO_ID_FIELD, SLO_INSTANCE_ID_FIELD } from '../../common/field_names/slo'; -import { ConfigSchema } from '../plugin'; import { ObservabilityRuleTypeRegistry } from './create_observability_rule_type_registry'; import { SLO_BURN_RATE_RULE_TYPE_ID } from '../../common/constants'; import { validateBurnRateRule } from '../components/burn_rate_rule_editor/validation'; @@ -86,8 +89,8 @@ const getDataViewId = (searchConfiguration?: SerializedSearchSourceFields) => : searchConfiguration?.index?.title; export const registerObservabilityRuleTypes = async ( - config: ConfigSchema, observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry, + uiSettings: IUiSettingsClient, logsExplorerLocator?: LocatorPublic ) => { observabilityRuleTypeRegistry.register({ @@ -117,7 +120,13 @@ export const registerObservabilityRuleTypes = async ( ), priority: 100, }); - + const validateCustomThresholdWithUiSettings = ({ + criteria, + searchConfiguration, + }: { + criteria: CustomMetricExpressionParams[]; + searchConfiguration: SerializedSearchSourceFields; + }) => validateCustomThreshold({ criteria, searchConfiguration, uiSettings }); observabilityRuleTypeRegistry.register({ id: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, description: i18n.translate( @@ -133,7 +142,7 @@ export const registerObservabilityRuleTypes = async ( ruleParamsExpression: lazy( () => import('../components/custom_threshold/custom_threshold_rule_expression') ), - validate: validateCustomThreshold, + validate: validateCustomThresholdWithUiSettings, defaultActionMessage: thresholdDefaultActionMessage, defaultRecoveryMessage: thresholdDefaultRecoveryMessage, requiresAppContext: false, diff --git a/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts b/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts index dfc6daa82d40d..9e3eab1e8a054 100644 --- a/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts +++ b/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts @@ -1056,7 +1056,7 @@ describe('The custom threshold alert type', () => { const { action } = mostRecentAction(instanceID); const reasons = action.reason; expect(reasons).toBe( - 'Average test.metric.1 is 1, above the threshold of 1; Average test.metric.2 is 3, above the threshold of 3. (duration: 1 min, data view: mockedDataViewName)' + 'Average test.metric.1 is 1, above or equal the threshold of 1; Average test.metric.2 is 3, above or equal the threshold of 3. (duration: 1 min, data view: mockedDataViewName)' ); }); }); diff --git a/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/check_missing_group.ts b/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/check_missing_group.ts index 6b8041b448484..c45120cc62fa3 100644 --- a/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/check_missing_group.ts +++ b/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/check_missing_group.ts @@ -10,7 +10,7 @@ import type { Logger } from '@kbn/logging'; import { isString, get, identity } from 'lodash'; import { CustomMetricExpressionParams } from '../../../../../common/custom_threshold_rule/types'; import type { BucketKey } from './get_data'; -import { calculateCurrentTimeframe, createBaseFilters } from './metric_query'; +import { calculateCurrentTimeFrame, createBaseFilters } from './metric_query'; export interface MissingGroupsRecord { key: string; @@ -31,8 +31,8 @@ export const checkMissingGroups = async ( if (missingGroups.length === 0) { return missingGroups; } - const currentTimeframe = calculateCurrentTimeframe(metricParams, timeframe); - const baseFilters = createBaseFilters(metricParams, currentTimeframe, timeFieldName, filterQuery); + const currentTimeFrame = calculateCurrentTimeFrame(metricParams, timeframe); + const baseFilters = createBaseFilters(currentTimeFrame, timeFieldName, filterQuery); const groupByFields = isString(groupBy) ? [groupBy] : groupBy ? groupBy : []; const searches = missingGroups.flatMap((group) => { diff --git a/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/create_custom_metrics_aggregations.ts b/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/create_custom_metrics_aggregations.ts index 255475c8d8a5c..3ab20c5a632c7 100644 --- a/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/create_custom_metrics_aggregations.ts +++ b/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/create_custom_metrics_aggregations.ts @@ -7,11 +7,17 @@ import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; import { isEmpty } from 'lodash'; -import { CustomThresholdExpressionMetric } from '../../../../../common/custom_threshold_rule/types'; +import { + Aggregators, + CustomThresholdExpressionMetric, +} from '../../../../../common/custom_threshold_rule/types'; +import { createRateAggsBuckets, createRateAggsBucketScript } from './create_rate_aggregation'; export const createCustomMetricsAggregations = ( id: string, customMetrics: CustomThresholdExpressionMetric[], + currentTimeFrame: { start: number; end: number }, + timeFieldName: string, equation?: string ) => { const bucketsPath: { [id: string]: string } = {}; @@ -30,6 +36,28 @@ export const createCustomMetricsAggregations = ( }, }; } + if (aggregation === Aggregators.P95 || aggregation === Aggregators.P99) { + bucketsPath[metric.name] = key; + return { + ...acc, + [key]: { + percentiles: { + field: metric.field, + percents: [aggregation === Aggregators.P95 ? 95 : 99], + keyed: true, + }, + }, + }; + } + + if (aggregation === Aggregators.RATE) { + bucketsPath[metric.name] = key; + return { + ...acc, + ...createRateAggsBuckets(currentTimeFrame, key, timeFieldName, metric.field || ''), + ...createRateAggsBucketScript(currentTimeFrame, key), + }; + } if (aggregation && metric.field) { bucketsPath[metric.name] = key; diff --git a/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/create_rate_aggregation.ts b/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/create_rate_aggregation.ts new file mode 100644 index 0000000000000..b68101d8e9b35 --- /dev/null +++ b/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/create_rate_aggregation.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 moment from 'moment'; +import { calculateRateTimeranges } from '../utils'; +export const createRateAggsBucketScript = ( + timeframe: { start: number; end: number }, + id: string +) => { + const { intervalInSeconds } = calculateRateTimeranges({ + to: timeframe.end, + from: timeframe.start, + }); + return { + [id]: { + bucket_script: { + buckets_path: { + first: `${id}_first_bucket.maxValue`, + second: `${id}_second_bucket.maxValue`, + }, + script: `params.second > 0.0 && params.first > 0.0 && params.second > params.first ? (params.second - params.first) / ${intervalInSeconds}: 0`, + }, + }, + }; +}; + +export const createRateAggsBuckets = ( + timeframe: { start: number; end: number }, + id: string, + timeFieldName: string, + field: string +) => { + const { firstBucketRange, secondBucketRange } = calculateRateTimeranges({ + to: timeframe.end, + from: timeframe.start, + }); + + return { + [`${id}_first_bucket`]: { + filter: { + range: { + [timeFieldName]: { + gte: moment(firstBucketRange.from).toISOString(), + lt: moment(firstBucketRange.to).toISOString(), + }, + }, + }, + aggs: { maxValue: { max: { field } } }, + }, + [`${id}_second_bucket`]: { + filter: { + range: { + [timeFieldName]: { + gte: moment(secondBucketRange.from).toISOString(), + lt: moment(secondBucketRange.to).toISOString(), + }, + }, + }, + aggs: { maxValue: { max: { field } } }, + }, + }; +}; diff --git a/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/create_timerange.ts b/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/create_timerange.ts index a6c39adcd3204..0d82b5df637b8 100644 --- a/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/create_timerange.ts +++ b/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/create_timerange.ts @@ -10,11 +10,15 @@ import moment from 'moment'; export const createTimerange = ( interval: number, timeframe: { end: string; start: string }, - lastPeriodEnd?: number + lastPeriodEnd?: number, + isRateAgg?: boolean ) => { const end = moment(timeframe.end).valueOf(); let start = moment(timeframe.start).valueOf(); + const minimumBuckets = isRateAgg ? 2 : 1; + + interval = interval * minimumBuckets; start = start - interval; // Use lastPeriodEnd - interval when it's less than start diff --git a/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/evaluate_rule.ts b/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/evaluate_rule.ts index f5b3f0adf1bdd..59f5801613dd0 100644 --- a/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/evaluate_rule.ts +++ b/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/evaluate_rule.ts @@ -8,8 +8,11 @@ import moment from 'moment'; import { ElasticsearchClient } from '@kbn/core/server'; import type { Logger } from '@kbn/logging'; -import { CustomMetricExpressionParams } from '../../../../../common/custom_threshold_rule/types'; import { getIntervalInSeconds } from '../../../../../common/utils/get_interval_in_seconds'; +import { + Aggregators, + CustomMetricExpressionParams, +} from '../../../../../common/custom_threshold_rule/types'; import { AdditionalContext } from '../utils'; import { SearchConfigurationType } from '../types'; import { createTimerange } from './create_timerange'; @@ -50,7 +53,15 @@ export const evaluateRule = async metric.aggType === Aggregators.RATE + ); + const calculatedTimerange = createTimerange( + intervalAsMS, + timeframe, + lastPeriodEnd, + isRateAggregation + ); const currentValues = await getData( esClient, diff --git a/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/format_alert_result.ts b/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/format_alert_result.ts index c0220d89c9d98..02acac53023d0 100644 --- a/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/format_alert_result.ts +++ b/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/format_alert_result.ts @@ -6,8 +6,9 @@ */ import { i18n } from '@kbn/i18n'; +import { EventsAsUnit } from '../../../../../common/constants'; +import { metricValueFormatter } from '../../../../../common/custom_threshold_rule/metric_value_formatter'; import { Aggregators } from '../../../../../common/custom_threshold_rule/types'; -import { createFormatter } from '../../../../../common/custom_threshold_rule/formatters'; import { AVERAGE_I18N, CARDINALITY_I18N, @@ -15,6 +16,9 @@ import { DOCUMENT_COUNT_I18N, MAX_I18N, MIN_I18N, + PERCENTILE_95_I18N, + PERCENTILE_99_I18N, + RATE_I18N, SUM_I18N, } from '../translations'; import { Evaluation } from './evaluate_rule'; @@ -31,6 +35,12 @@ export const getLabel = (criterion: Evaluation) => { return DOCUMENT_COUNT_I18N; case Aggregators.AVERAGE: return AVERAGE_I18N(criterion.metrics[0].field!); + case Aggregators.P95: + return PERCENTILE_95_I18N(criterion.metrics[0].field!); + case Aggregators.P99: + return PERCENTILE_99_I18N(criterion.metrics[0].field!); + case Aggregators.RATE: + return RATE_I18N(criterion.metrics[0].field!); case Aggregators.MAX: return MAX_I18N(criterion.metrics[0].field!); case Aggregators.MIN: @@ -51,21 +61,27 @@ export const formatAlertResult = (evaluationResult: Evaluation): FormattedEvalua { defaultMessage: '[NO DATA]' } ); - let formatter = createFormatter('highPrecision'); const label = getLabel(evaluationResult); - if (metrics.length === 1 && metrics[0].field && metrics[0].field.endsWith('.pct')) { - formatter = createFormatter('percent'); - } + const perSecIfRate = metrics[0].aggType === Aggregators.RATE ? '/s' : ''; + const eventsAsUnit = + metrics[0].aggType === Aggregators.RATE && + !metrics[0].field?.endsWith('.pct') && + !metrics[0].field?.endsWith('.bytes') + ? ` ${EventsAsUnit}` + : ''; + const rateUnitPerSec = eventsAsUnit + perSecIfRate; return { ...evaluationResult, currentValue: - currentValue !== null && currentValue !== undefined ? formatter(currentValue) : noDataValue, + currentValue !== null && currentValue !== undefined + ? metricValueFormatter(currentValue, metrics[0].field) + rateUnitPerSec + : noDataValue, label: label || CUSTOM_EQUATION_I18N, threshold: Array.isArray(threshold) - ? threshold.map((v: number) => formatter(v)) - : [formatter(threshold)], + ? threshold.map((v: number) => metricValueFormatter(v, metrics[0].field) + rateUnitPerSec) + : [metricValueFormatter(currentValue, metrics[0].field) + rateUnitPerSec], comparator, }; }; diff --git a/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/metric_query.ts b/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/metric_query.ts index 6471926522929..3cc1eee92fec9 100644 --- a/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/metric_query.ts +++ b/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/metric_query.ts @@ -6,7 +6,10 @@ */ import moment from 'moment'; -import { CustomMetricExpressionParams } from '../../../../../common/custom_threshold_rule/types'; +import { + Aggregators, + CustomMetricExpressionParams, +} from '../../../../../common/custom_threshold_rule/types'; import { createCustomMetricsAggregations } from './create_custom_metrics_aggregations'; import { CONTAINER_ID, @@ -19,16 +22,23 @@ import { createBucketSelector } from './create_bucket_selector'; import { wrapInCurrentPeriod } from './wrap_in_period'; import { getParsedFilterQuery } from '../../../../utils/get_parsed_filtered_query'; -export const calculateCurrentTimeframe = ( +export const calculateCurrentTimeFrame = ( metricParams: CustomMetricExpressionParams, timeframe: { start: number; end: number } -) => ({ - ...timeframe, - start: moment(timeframe.end).subtract(metricParams.timeSize, metricParams.timeUnit).valueOf(), -}); +) => { + const isRateAgg = metricParams.metrics.some((metric) => metric.aggType === Aggregators.RATE); + return { + ...timeframe, + start: moment(timeframe.end) + .subtract( + isRateAgg ? metricParams.timeSize * 2 : metricParams.timeSize, + metricParams.timeUnit + ) + .valueOf(), + }; +}; export const createBaseFilters = ( - metricParams: CustomMetricExpressionParams, timeframe: { start: number; end: number }, timeFieldName: string, filterQuery?: string @@ -63,14 +73,16 @@ export const getElasticsearchMetricQuery = ( ) => { // We need to make a timeframe that represents the current timeframe as opposed // to the total timeframe (which includes the last period). - const currentTimeframe = { - ...calculateCurrentTimeframe(metricParams, timeframe), + const currentTimeFrame = { + ...calculateCurrentTimeFrame(metricParams, timeframe), timeFieldName, }; const metricAggregations = createCustomMetricsAggregations( 'aggregatedValue', metricParams.metrics, + currentTimeFrame, + timeFieldName, metricParams.equation ); @@ -82,7 +94,7 @@ export const getElasticsearchMetricQuery = ( lastPeriodEnd ); - const currentPeriod = wrapInCurrentPeriod(currentTimeframe, metricAggregations); + const currentPeriod = wrapInCurrentPeriod(currentTimeFrame, metricAggregations); const containerIncludesList = ['container.*']; const containerExcludesList = [ @@ -184,7 +196,7 @@ export const getElasticsearchMetricQuery = ( aggs.groupings.composite.after = afterKey; } - const baseFilters = createBaseFilters(metricParams, timeframe, timeFieldName, filterQuery); + const baseFilters = createBaseFilters(timeframe, timeFieldName, filterQuery); return { track_total_hits: true, diff --git a/x-pack/plugins/observability/server/lib/rules/custom_threshold/messages.ts b/x-pack/plugins/observability/server/lib/rules/custom_threshold/messages.ts index 973ce8f57093a..ca160d06b6573 100644 --- a/x-pack/plugins/observability/server/lib/rules/custom_threshold/messages.ts +++ b/x-pack/plugins/observability/server/lib/rules/custom_threshold/messages.ts @@ -16,6 +16,8 @@ import { BETWEEN_TEXT, NOT_BETWEEN_TEXT, CUSTOM_EQUATION_I18N, + ABOVE_OR_EQ_TEXT, + BELOW_OR_EQ_TEXT, } from './translations'; import { UNGROUPED_FACTORY_KEY } from './constants'; @@ -33,11 +35,13 @@ const recoveredComparatorToI18n = ( case Comparator.OUTSIDE_RANGE: return BETWEEN_TEXT; case Comparator.GT: + return ABOVE_TEXT; case Comparator.GT_OR_EQ: - return BELOW_TEXT; + return ABOVE_OR_EQ_TEXT; case Comparator.LT: + return BELOW_TEXT; case Comparator.LT_OR_EQ: - return ABOVE_TEXT; + return BELOW_OR_EQ_TEXT; } }; @@ -48,11 +52,13 @@ const alertComparatorToI18n = (comparator: Comparator) => { case Comparator.OUTSIDE_RANGE: return NOT_BETWEEN_TEXT; case Comparator.GT: - case Comparator.GT_OR_EQ: return ABOVE_TEXT; + case Comparator.GT_OR_EQ: + return ABOVE_OR_EQ_TEXT; case Comparator.LT: - case Comparator.LT_OR_EQ: return BELOW_TEXT; + case Comparator.LT_OR_EQ: + return BELOW_OR_EQ_TEXT; } }; diff --git a/x-pack/plugins/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts b/x-pack/plugins/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts index f0e0ede0945c8..5e9c2e0cea019 100644 --- a/x-pack/plugins/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts +++ b/x-pack/plugins/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts @@ -18,7 +18,7 @@ import { createLifecycleExecutor, IRuleDataClient } from '@kbn/rule-registry-plu import { LicenseType } from '@kbn/licensing-plugin/server'; import { EsQueryRuleParamsExtractedParams } from '@kbn/stack-alerts-plugin/server/rule_types/es_query/rule_type_params'; import { observabilityFeatureId, observabilityPaths } from '../../../../common'; -import { Comparator } from '../../../../common/custom_threshold_rule/types'; +import { Aggregators, Comparator } from '../../../../common/custom_threshold_rule/types'; import { THRESHOLD_RULE_REGISTRATION_CONTEXT } from '../../../common/constants'; import { @@ -76,6 +76,8 @@ export function thresholdRuleType( timeUnit: schema.string(), timeSize: schema.number(), }; + const allowedAggregators = Object.values(Aggregators); + allowedAggregators.splice(Object.values(Aggregators).indexOf(Aggregators.COUNT), 1); const customCriterion = schema.object({ ...baseCriterion, @@ -85,7 +87,7 @@ export function thresholdRuleType( schema.oneOf([ schema.object({ name: schema.string(), - aggType: oneOfLiterals(['avg', 'sum', 'max', 'min', 'cardinality']), + aggType: oneOfLiterals(allowedAggregators), field: schema.string(), filter: schema.never(), }), diff --git a/x-pack/plugins/observability/server/lib/rules/custom_threshold/translations.ts b/x-pack/plugins/observability/server/lib/rules/custom_threshold/translations.ts index 301e793150dc6..020229c27a4ad 100644 --- a/x-pack/plugins/observability/server/lib/rules/custom_threshold/translations.ts +++ b/x-pack/plugins/observability/server/lib/rules/custom_threshold/translations.ts @@ -22,6 +22,30 @@ export const AVERAGE_I18N = (metric: string) => }, }); +export const PERCENTILE_99_I18N = (metric: string) => + i18n.translate('xpack.observability.customThreshold.rule.aggregators.p99', { + defaultMessage: '99th percentile of {metric}', + values: { + metric, + }, + }); + +export const PERCENTILE_95_I18N = (metric: string) => + i18n.translate('xpack.observability.customThreshold.rule.aggregators.p95', { + defaultMessage: '95th percentile of {metric}', + values: { + metric, + }, + }); + +export const RATE_I18N = (metric: string) => + i18n.translate('xpack.observability.customThreshold.rule.aggregators.rate', { + defaultMessage: 'Rate of {metric}', + values: { + metric, + }, + }); + export const MAX_I18N = (metric: string) => i18n.translate('xpack.observability.customThreshold.rule.aggregators.max', { defaultMessage: 'Max {metric}', @@ -70,6 +94,13 @@ export const BELOW_TEXT = i18n.translate( } ); +export const BELOW_OR_EQ_TEXT = i18n.translate( + 'xpack.observability.customThreshold.rule.threshold.belowOrEqual', + { + defaultMessage: 'below or equal', + } +); + export const ABOVE_TEXT = i18n.translate( 'xpack.observability.customThreshold.rule.threshold.above', { @@ -77,6 +108,13 @@ export const ABOVE_TEXT = i18n.translate( } ); +export const ABOVE_OR_EQ_TEXT = i18n.translate( + 'xpack.observability.customThreshold.rule.threshold.aboveOrEqual', + { + defaultMessage: 'above or equal', + } +); + export const BETWEEN_TEXT = i18n.translate( 'xpack.observability.customThreshold.rule.threshold.between', { diff --git a/x-pack/plugins/observability_ai_assistant/public/components/insight/actions_menu.tsx b/x-pack/plugins/observability_ai_assistant/public/components/insight/actions_menu.tsx new file mode 100644 index 0000000000000..2cddc557ee1b6 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/insight/actions_menu.tsx @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useState } from 'react'; +import { EuiButtonIcon, EuiContextMenu, EuiPanel, EuiPopover } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { UseGenAIConnectorsResult } from '../../hooks/use_genai_connectors'; +import { ConnectorSelectorBase } from '../connector_selector/connector_selector_base'; + +export function ActionsMenu({ + connectors, + onEditPrompt, +}: { + connectors: UseGenAIConnectorsResult; + onEditPrompt: () => void; +}) { + const [isPopoverOpen, setPopover] = useState(false); + + const onButtonClick = () => { + setPopover(!isPopoverOpen); + }; + + const closePopover = () => { + setPopover(false); + }; + + const panels = [ + { + id: 0, + title: 'Actions', + items: [ + { + name: ( +
+ {i18n.translate('xpack.observabilityAiAssistant.insight.actions.connector', { + defaultMessage: 'Connector', + })}{' '} + + {connectors.connectors?.find(({ id }) => id === connectors.selectedConnector)?.name} + +
+ ), + icon: 'wrench', + panel: 1, + }, + { + name: i18n.translate('xpack.observabilityAiAssistant.insight.actions.editPrompt', { + defaultMessage: 'Edit prompt', + }), + icon: 'documentEdit', + onClick: () => { + onEditPrompt(); + closePopover(); + }, + }, + ], + }, + { + id: 1, + title: i18n.translate('xpack.observabilityAiAssistant.insight.actions.connector', { + defaultMessage: 'Connector', + }), + content: ( + + + + ), + }, + ]; + + const button = ( + + ); + + return ( + + + + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx index bfe01db7fb49f..2d57ff92cd5bc 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx @@ -4,8 +4,18 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { last } from 'lodash'; +import { + EuiHorizontalRule, + EuiButtonEmpty, + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiText, + EuiTextArea, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { cloneDeep, last } from 'lodash'; import React, { useEffect, useRef, useState } from 'react'; import { MessageRole, type Message } from '../../../common/types'; import { sendEvent, TELEMETRY } from '../../analytics'; @@ -21,12 +31,16 @@ import { RegenerateResponseButton } from '../buttons/regenerate_response_button' import { StartChatButton } from '../buttons/start_chat_button'; import { StopGeneratingButton } from '../buttons/stop_generating_button'; import { ChatFlyout } from '../chat/chat_flyout'; -import { ConnectorSelectorBase } from '../connector_selector/connector_selector_base'; import { FeedbackButtons } from '../feedback_buttons'; import { MessagePanel } from '../message_panel/message_panel'; import { MessageText } from '../message_panel/message_text'; import { MissingCredentialsCallout } from '../missing_credentials_callout'; import { InsightBase } from './insight_base'; +import { ActionsMenu } from './actions_menu'; + +function getLastMessageOfType(messages: Message[], role: MessageRole) { + return last(messages.filter((msg) => msg.message.role === role)); +} function ChatContent({ title: defaultTitle, @@ -48,9 +62,7 @@ function ChatContent({ persist: false, }); - const lastAssistantResponse = last( - messages.filter((message) => message.message.role === MessageRole.Assistant) - ); + const lastAssistantResponse = getLastMessageOfType(messages, MessageRole.Assistant); useEffect(() => { next(initialMessagesRef.current); @@ -122,6 +134,63 @@ function ChatContent({ ); } +function PromptEdit({ + initialPrompt, + onSend, + onCancel, +}: { + initialPrompt: string; + onSend: (updatedPrompt: string) => void; + onCancel: () => void; +}) { + const [prompt, setPrompt] = useState(initialPrompt); + + return ( + + + { + if (textarea) { + setTimeout(() => textarea.focus()); + } + }} + fullWidth={true} + defaultValue={prompt} + onChange={(ev) => { + setPrompt(ev.target.value); + }} + /> + + + + + + onSend(prompt)} + /> + + + ); +} + export interface InsightProps { messages: Message[]; title: string; @@ -129,7 +198,11 @@ export interface InsightProps { } export function Insight({ messages, title, dataTestSubj }: InsightProps) { + const [initialMessages, setInitialMessages] = useState(messages); + const [isEditingPrompt, setEditingPrompt] = useState(false); + const [isInsightOpen, setInsightOpen] = useState(false); const [hasOpened, setHasOpened] = useState(false); + const [isPromptUpdated, setIsPromptUpdated] = useState(false); const connectors = useGenAIConnectors(); @@ -142,18 +215,85 @@ export function Insight({ messages, title, dataTestSubj }: InsightProps) { [service] ); + const handleSend = (newPrompt: string) => { + const clonedMessages = cloneDeep(messages); + const userMessage = getLastMessageOfType(clonedMessages, MessageRole.User); + if (!userMessage) return false; + + userMessage.message.content = newPrompt; + setIsPromptUpdated(true); + setInitialMessages(clonedMessages); + setEditingPrompt(false); + return true; + }; + + const handleCancel = () => { + setEditingPrompt(false); + setInsightOpen(false); + setHasOpened(false); + }; + const { services: { http }, } = useKibana(); let children: React.ReactNode = null; - if (hasOpened && connectors.selectedConnector) { + if ( + connectors.selectedConnector && + ((!isInsightOpen && hasOpened) || (isInsightOpen && !isEditingPrompt)) + ) { children = ( - + {isPromptUpdated ? ( + <> + + + + {i18n.translate('xpack.observabilityAiAssistant.insightModifiedPrompt', { + defaultMessage: 'This insight has been modified.', + })} + + + + { + setIsPromptUpdated(false); + setHasOpened(false); + setInsightOpen(false); + setInitialMessages(messages); + }} + > + + {i18n.translate('xpack.observabilityAiAssistant.resetDefaultPrompt', { + defaultMessage: 'Reset to default', + })} + + + + + + + + + ) : null} + + + + ); + } else if (isEditingPrompt) { + children = ( + ); } else if (!connectors.loading && !connectors.connectors?.length) { @@ -166,11 +306,24 @@ export function Insight({ messages, title, dataTestSubj }: InsightProps) { { - setHasOpened((prevHasOpened) => prevHasOpened || isOpen); + setHasOpened((prevHasOpened) => { + if (isEditingPrompt) return false; + return prevHasOpened || isOpen; + }); + setInsightOpen(isOpen); }} - controls={} + controls={ + { + setEditingPrompt(true); + setInsightOpen(true); + }} + /> + } loading={connectors.loading || chatService.loading} dataTestSubj={dataTestSubj} + isOpen={isInsightOpen} > {chatService.value ? ( diff --git a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.stories.tsx b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.stories.tsx index f34bb0ce3034d..b0a09caa805ae 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.stories.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.stories.tsx @@ -12,12 +12,12 @@ import { FindActionResult } from '@kbn/actions-plugin/server'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { InsightBase as Component, InsightBaseProps } from './insight_base'; import { KibanaReactStorybookDecorator } from '../../utils/storybook_decorator'; -import { ConnectorSelectorBase } from '../connector_selector/connector_selector_base'; import { MessagePanel } from '../message_panel/message_panel'; import { MessageText } from '../message_panel/message_text'; import { FeedbackButtons } from '../feedback_buttons'; import { RegenerateResponseButton } from '../buttons/regenerate_response_button'; import { StartChatButton } from '../buttons/start_chat_button'; +import { ActionsMenu } from './actions_menu'; export default { component: Component, @@ -37,17 +37,18 @@ const defaultProps: InsightBaseProps = { ], loading: false, controls: ( - {}} - reloadConnectors={() => {}} + ] as FindActionResult[], + selectedConnector: 'gpt-4', + loading: false, + selectConnector: () => {}, + reloadConnectors: () => {}, + }} + onEditPrompt={() => {}} /> ), onToggle: () => {}, @@ -87,6 +88,7 @@ Morbi non faucibus massa. Aliquam sed augue in eros ornare luctus sit amet cursu } /> ), + isOpen: false, }; export const Insight = Template.bind({}); diff --git a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.tsx b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.tsx index 702ce9296c132..62310b2aed555 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.tsx @@ -12,6 +12,7 @@ import { EuiContextMenuPanel, EuiFlexGroup, EuiFlexItem, + EuiIconTip, EuiPanel, EuiPopover, EuiSpacer, @@ -30,6 +31,7 @@ export interface InsightBaseProps { actions?: Array<{ id: string; label: string; icon?: string; handler: () => void }>; onToggle: (isOpen: boolean) => void; children: React.ReactNode; + isOpen: boolean; loading?: boolean; dataTestSubj?: string; } @@ -44,6 +46,7 @@ export function InsightBase({ actions, onToggle, loading, + isOpen, dataTestSubj = 'obsAiAssistantInsightButton', }: InsightBaseProps) { const { euiTheme } = useEuiTheme(); @@ -66,10 +69,19 @@ export function InsightBase({ - -
{title}
-
+ + +
{title}
+
+ +
{description} @@ -78,6 +90,7 @@ export function InsightBase({ } isLoading={loading} isDisabled={loading} + forceState={isOpen ? 'open' : 'closed'} extraAction={ actions?.length || controls ? ( diff --git a/x-pack/plugins/observability_shared/public/components/load_when_in_view/get_load_when_in_view_lazy.tsx b/x-pack/plugins/observability_shared/public/components/load_when_in_view/get_load_when_in_view_lazy.tsx index eb83b2682f341..b1cba9b58e182 100644 --- a/x-pack/plugins/observability_shared/public/components/load_when_in_view/get_load_when_in_view_lazy.tsx +++ b/x-pack/plugins/observability_shared/public/components/load_when_in_view/get_load_when_in_view_lazy.tsx @@ -7,7 +7,7 @@ import React, { lazy, Suspense } from 'react'; import { EuiLoadingSpinner } from '@elastic/eui'; -import { LoadWhenInViewProps } from './load_when_in_view'; +import type { LoadWhenInViewProps } from './load_when_in_view'; const LoadWhenInViewLazy = lazy(() => import('./load_when_in_view')); diff --git a/x-pack/plugins/observability_shared/public/components/tags_list/tags_list.tsx b/x-pack/plugins/observability_shared/public/components/tags_list/tags_list.tsx new file mode 100644 index 0000000000000..6599a1eec32ff --- /dev/null +++ b/x-pack/plugins/observability_shared/public/components/tags_list/tags_list.tsx @@ -0,0 +1,136 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; +import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiText, EuiToolTip } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { EuiBadgeProps } from '@elastic/eui/src/components/badge/badge'; + +export interface TagsListProps { + onClick?: (tag: string) => void; + tags?: string[]; + numberOfTagsToDisplay?: number; + color?: EuiBadgeProps['color']; + ignoreEmpty?: boolean; +} + +const getFilterLabel = (tag: string) => { + return i18n.translate('xpack.observabilityShared.getFilterLabel.filter', { + defaultMessage: 'Click to filter list with tag {tag}', + values: { + tag, + }, + }); +}; + +const TagsList = ({ + ignoreEmpty, + tags, + numberOfTagsToDisplay = 3, + onClick, + color = 'hollow', +}: TagsListProps) => { + const [toDisplay, setToDisplay] = useState(numberOfTagsToDisplay); + + if (!tags || tags.length === 0) { + if (ignoreEmpty) { + return null; + } + return ( + + {i18n.translate('xpack.observabilityShared.tagsList.TextLabel', { defaultMessage: '--' })} + + ); + } + + const tagsToDisplay = tags.slice(0, toDisplay); + + return ( + + {tagsToDisplay.map((tag) => ( + // filtering only makes sense in monitor list, where we have summary + + {onClick ? ( + { + onClick(tag); + }} + onClickAriaLabel={getFilterLabel(tag)} + color={color} + className="eui-textTruncate" + style={{ maxWidth: 120 }} + > + {tag} + + ) : ( + + {tag} + + )} + + ))} + {tags.length > toDisplay && ( + + + {tags.slice(toDisplay, tags.length).map((tag) => ( + + {tag} + + ))} + + } + > + { + setToDisplay(tags.length); + }} + onClickAriaLabel={EXPAND_TAGS_LABEL} + > + +{tags.length - toDisplay} + + + + )} + {toDisplay > 3 && ( + + + { + setToDisplay(3); + }} + onClickAriaLabel={COLLAPSE_TAGS_LABEL} + > + -{tags.length - 3} + + + + )} + + ); +}; + +// eslint-disable-next-line import/no-default-export +export default TagsList; + +const EXPAND_TAGS_LABEL = i18n.translate('xpack.observabilityShared.tagsList.expand', { + defaultMessage: 'Click to view remaining tags', +}); + +const COLLAPSE_TAGS_LABEL = i18n.translate('xpack.observabilityShared.tagsList.collapse', { + defaultMessage: 'Click to collapse tags', +}); diff --git a/x-pack/plugins/observability_shared/public/components/tags_list/tags_list_lazy.tsx b/x-pack/plugins/observability_shared/public/components/tags_list/tags_list_lazy.tsx new file mode 100644 index 0000000000000..6e161cda52686 --- /dev/null +++ b/x-pack/plugins/observability_shared/public/components/tags_list/tags_list_lazy.tsx @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { lazy, Suspense } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import type { TagsListProps } from './tags_list'; + +const TagsListLazy = lazy(() => import('./tags_list')); + +export function TagsList(props: TagsListProps) { + return ( + }> + + + ); +} diff --git a/x-pack/plugins/observability_shared/public/index.ts b/x-pack/plugins/observability_shared/public/index.ts index 3c9d2fe5a8eec..9b4ef62271697 100644 --- a/x-pack/plugins/observability_shared/public/index.ts +++ b/x-pack/plugins/observability_shared/public/index.ts @@ -35,6 +35,8 @@ export { } from './components/section/section'; export type { SectionLinkProps } from './components/section/section'; export { LoadWhenInView } from './components/load_when_in_view/get_load_when_in_view_lazy'; +export { TagsList } from './components/tags_list/tags_list_lazy'; +export type { TagsListProps } from './components/tags_list/tags_list'; export { TechnicalPreviewBadge } from './components/technical_preview_badge/technical_preview_badge'; diff --git a/x-pack/plugins/profiling/server/lib/setup/README.md b/x-pack/plugins/profiling/server/lib/setup/README.md index b1eb7dc241075..dfab97d508c74 100644 --- a/x-pack/plugins/profiling/server/lib/setup/README.md +++ b/x-pack/plugins/profiling/server/lib/setup/README.md @@ -26,7 +26,7 @@ Build and push a Kibana image with the latest changes. Choose a unique identifier for the build, then: ``` -node scripts/build --docker-images --skip-docker-ubi --skip-docker-ubuntu +node scripts/build --docker-images --skip-docker-ubi --skip-docker-ubuntu --skip-docker-fips docker tag docker.elastic.co/kibana-ci/kibana-cloud:8.7.0-SNAPSHOT docker.elastic.co/observability-ci/kibana: docker push docker.elastic.co/observability-ci/kibana: ``` 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 index afed418ec8d3d..2367167b49697 100644 --- 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 @@ -208,6 +208,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, TEST_ALERT_1: { alertId: 'TEST_ALERT_1', @@ -216,6 +217,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, }, trackedAlertsRecovered: {}, @@ -358,6 +360,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, TEST_ALERT_1: { alertId: 'TEST_ALERT_1', @@ -366,6 +369,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, }, trackedAlertsRecovered: {}, @@ -490,6 +494,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, TEST_ALERT_1: { alertId: 'TEST_ALERT_1', @@ -498,6 +503,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, }, trackedAlertsRecovered: {}, @@ -732,6 +738,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, TEST_ALERT_1: { alertId: 'TEST_ALERT_1', @@ -740,6 +747,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, }, trackedAlertsRecovered: {}, @@ -850,6 +858,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, TEST_ALERT_1: { alertId: 'TEST_ALERT_1', @@ -858,6 +867,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, }, trackedAlerts: {}, @@ -963,6 +973,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, TEST_ALERT_1: { alertId: 'TEST_ALERT_1', @@ -971,6 +982,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, }, trackedAlertsRecovered: {}, @@ -1077,6 +1089,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, }, trackedAlertsRecovered: { @@ -1087,6 +1100,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, }, }, @@ -1258,6 +1272,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, TEST_ALERT_1: { alertId: 'TEST_ALERT_1', @@ -1266,6 +1281,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, }, trackedAlertsRecovered: {}, @@ -1388,6 +1404,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, TEST_ALERT_1: { alertId: 'TEST_ALERT_1', @@ -1396,6 +1413,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, }, trackedAlertsRecovered: {}, @@ -1570,6 +1588,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: flapping, flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, TEST_ALERT_1: { alertId: 'TEST_ALERT_1', @@ -1578,6 +1597,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [false, false], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, TEST_ALERT_2: { alertId: 'TEST_ALERT_2', @@ -1586,6 +1606,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: flapping, flapping: true, pendingRecoveredCount: 0, + activeCount: 0, }, TEST_ALERT_3: { alertId: 'TEST_ALERT_3', @@ -1594,6 +1615,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [false, false], flapping: true, pendingRecoveredCount: 0, + activeCount: 0, }, }, trackedAlertsRecovered: {}, @@ -1604,6 +1626,7 @@ describe('createLifecycleExecutor', () => { expect(serializedAlerts.state.trackedAlerts).toEqual({ TEST_ALERT_0: { + activeCount: 1, alertId: 'TEST_ALERT_0', alertUuid: 'TEST_ALERT_0_UUID', flapping: true, @@ -1612,6 +1635,7 @@ describe('createLifecycleExecutor', () => { started: '2020-01-01T12:00:00.000Z', }, TEST_ALERT_1: { + activeCount: 1, alertId: 'TEST_ALERT_1', alertUuid: 'TEST_ALERT_1_UUID', flapping: false, @@ -1620,6 +1644,7 @@ describe('createLifecycleExecutor', () => { started: '2020-01-02T12:00:00.000Z', }, TEST_ALERT_2: { + activeCount: 1, alertId: 'TEST_ALERT_2', alertUuid: 'TEST_ALERT_2_UUID', flapping: true, @@ -1628,6 +1653,7 @@ describe('createLifecycleExecutor', () => { started: '2020-01-01T12:00:00.000Z', }, TEST_ALERT_3: { + activeCount: 1, alertId: 'TEST_ALERT_3', alertUuid: 'TEST_ALERT_3_UUID', flapping: true, @@ -1786,6 +1812,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [true, true, true, true], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, TEST_ALERT_1: { alertId: 'TEST_ALERT_1', @@ -1794,6 +1821,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: notFlapping, flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, TEST_ALERT_2: { alertId: 'TEST_ALERT_2', @@ -1802,6 +1830,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: [true, true], flapping: true, pendingRecoveredCount: 0, + activeCount: 0, }, TEST_ALERT_3: { alertId: 'TEST_ALERT_3', @@ -1810,6 +1839,7 @@ describe('createLifecycleExecutor', () => { flappingHistory: notFlapping, flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, }, trackedAlertsRecovered: {}, @@ -1820,6 +1850,7 @@ describe('createLifecycleExecutor', () => { expect(serializedAlerts.state.trackedAlerts).toEqual({ TEST_ALERT_2: { + activeCount: 0, alertId: 'TEST_ALERT_2', alertUuid: 'TEST_ALERT_2_UUID', flapping: true, @@ -1831,6 +1862,7 @@ describe('createLifecycleExecutor', () => { expect(serializedAlerts.state.trackedAlertsRecovered).toEqual({ TEST_ALERT_0: { + activeCount: 0, alertId: 'TEST_ALERT_0', alertUuid: 'TEST_ALERT_0_UUID', flapping: true, @@ -1839,6 +1871,7 @@ describe('createLifecycleExecutor', () => { started: '2020-01-01T12:00:00.000Z', }, TEST_ALERT_1: { + activeCount: 0, alertId: 'TEST_ALERT_1', alertUuid: 'TEST_ALERT_1_UUID', flapping: false, @@ -1847,6 +1880,7 @@ describe('createLifecycleExecutor', () => { started: '2020-01-02T12:00:00.000Z', }, TEST_ALERT_3: { + activeCount: 0, alertId: 'TEST_ALERT_3', alertUuid: 'TEST_ALERT_3_UUID', flapping: false, diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts index 91d30fae7b3dc..4bd3b912ae67d 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts @@ -290,7 +290,7 @@ export const createLifecycleExecutor = trackedAlertRecoveredIds ); - const { alertUuid, started, flapping, pendingRecoveredCount } = !isNew + const { alertUuid, started, flapping, pendingRecoveredCount, activeCount } = !isNew ? state.trackedAlerts[alertId] : { alertUuid: lifecycleAlertServices.getAlertUuid(alertId), @@ -299,6 +299,7 @@ export const createLifecycleExecutor = ? state.trackedAlertsRecovered[alertId].flapping : false, pendingRecoveredCount: 0, + activeCount: 0, }; const event: ParsedTechnicalFields & ParsedExperimentalFields = { @@ -342,16 +343,20 @@ export const createLifecycleExecutor = flappingHistory, flapping, pendingRecoveredCount, + activeCount, }; }); const trackedEventsToIndex = makeEventsDataMapFor(trackedAlertIds); const newEventsToIndex = makeEventsDataMapFor(newAlertIds); const trackedRecoveredEventsToIndex = makeEventsDataMapFor(trackedAlertRecoveredIds); - const allEventsToIndex = [ - ...getAlertsForNotification(flappingSettings, trackedEventsToIndex), - ...newEventsToIndex, - ]; + const allEventsToIndex = getAlertsForNotification( + flappingSettings, + rule.alertDelay?.active ?? 0, + trackedEventsToIndex, + newEventsToIndex, + { maintenanceWindowIds, timestamp: commonRuleFields[TIMESTAMP] } + ); // Only write alerts if: // - writing is enabled @@ -392,18 +397,34 @@ export const createLifecycleExecutor = } const nextTrackedAlerts = Object.fromEntries( - allEventsToIndex + [...newEventsToIndex, ...trackedEventsToIndex] .filter(({ event }) => event[ALERT_STATUS] !== ALERT_STATUS_RECOVERED) - .map(({ event, flappingHistory, flapping: isCurrentlyFlapping, pendingRecoveredCount }) => { - 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 }, - ]; - }) + .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( @@ -416,16 +437,32 @@ export const createLifecycleExecutor = event[ALERT_STATUS] === ALERT_STATUS_RECOVERED && (flapping || flappingHistory.filter((f: boolean) => f).length > 0) ) - .map(({ event, flappingHistory, flapping: isCurrentlyFlapping, pendingRecoveredCount }) => { - 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 }, - ]; - }) + .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 { diff --git a/x-pack/plugins/rule_registry/server/utils/get_alerts_for_notification.test.ts b/x-pack/plugins/rule_registry/server/utils/get_alerts_for_notification.test.ts index b3047303bcb08..abb9ebba6d016 100644 --- a/x-pack/plugins/rule_registry/server/utils/get_alerts_for_notification.test.ts +++ b/x-pack/plugins/rule_registry/server/utils/get_alerts_for_notification.test.ts @@ -14,12 +14,17 @@ import { cloneDeep } from 'lodash'; import { getAlertsForNotification } from './get_alerts_for_notification'; describe('getAlertsForNotification', () => { + const newEventParams = { + maintenanceWindowIds: ['maintenance-window-id'], + timestamp: 'timestamp', + }; const alert1 = { event: { 'kibana.alert.status': ALERT_STATUS_RECOVERED, }, flapping: true, pendingRecoveredCount: 3, + activeCount: 3, }; const alert2 = { event: { @@ -40,13 +45,38 @@ describe('getAlertsForNotification', () => { pendingRecoveredCount: 4, flappingHistory: [true, true], }; + const alert5 = { + event: { + 'kibana.alert.status': ALERT_STATUS_ACTIVE, + }, + activeCount: 1, + pendingRecoveredCount: 0, + flappingHistory: [], + }; test('should set pendingRecoveredCount to zero for all active alerts', () => { - const trackedEvents = [alert4]; - expect(getAlertsForNotification(DEFAULT_FLAPPING_SETTINGS, trackedEvents)) - .toMatchInlineSnapshot(` + const trackedEvents = cloneDeep([alert4]); + const newEvents = cloneDeep([alert5]); + expect( + getAlertsForNotification( + DEFAULT_FLAPPING_SETTINGS, + 0, + trackedEvents, + newEvents, + newEventParams + ) + ).toMatchInlineSnapshot(` Array [ Object { + "activeCount": 2, + "event": Object { + "kibana.alert.status": "active", + }, + "flappingHistory": Array [], + "pendingRecoveredCount": 0, + }, + Object { + "activeCount": 1, "event": Object { "kibana.alert.status": "active", }, @@ -62,10 +92,12 @@ describe('getAlertsForNotification', () => { test('should not remove alerts if the num of recovered alerts is not at the limit', () => { const trackedEvents = cloneDeep([alert1, alert2, alert3]); - expect(getAlertsForNotification(DEFAULT_FLAPPING_SETTINGS, trackedEvents)) - .toMatchInlineSnapshot(` + expect( + getAlertsForNotification(DEFAULT_FLAPPING_SETTINGS, 0, trackedEvents, [], newEventParams) + ).toMatchInlineSnapshot(` Array [ Object { + "activeCount": 0, "event": Object { "kibana.alert.status": "recovered", }, @@ -73,12 +105,14 @@ describe('getAlertsForNotification', () => { "pendingRecoveredCount": 0, }, Object { + "activeCount": 0, "event": Object { "kibana.alert.status": "recovered", }, "flapping": false, }, Object { + "activeCount": 0, "event": Object { "event.action": "active", "kibana.alert.status": "active", @@ -92,10 +126,12 @@ describe('getAlertsForNotification', () => { test('should reset counts and not modify alerts if flapping is disabled', () => { const trackedEvents = cloneDeep([alert1, alert2, alert3]); - expect(getAlertsForNotification(DISABLE_FLAPPING_SETTINGS, trackedEvents)) - .toMatchInlineSnapshot(` + expect( + getAlertsForNotification(DISABLE_FLAPPING_SETTINGS, 0, trackedEvents, [], newEventParams) + ).toMatchInlineSnapshot(` Array [ Object { + "activeCount": 0, "event": Object { "kibana.alert.status": "recovered", }, @@ -103,6 +139,7 @@ describe('getAlertsForNotification', () => { "pendingRecoveredCount": 0, }, Object { + "activeCount": 0, "event": Object { "kibana.alert.status": "recovered", }, @@ -110,6 +147,7 @@ describe('getAlertsForNotification', () => { "pendingRecoveredCount": 0, }, Object { + "activeCount": 0, "event": Object { "kibana.alert.status": "recovered", }, @@ -119,4 +157,106 @@ describe('getAlertsForNotification', () => { ] `); }); + + test('should increment activeCount for all active alerts', () => { + const trackedEvents = cloneDeep([alert4]); + const newEvents = cloneDeep([alert5]); + expect( + getAlertsForNotification( + DEFAULT_FLAPPING_SETTINGS, + 0, + trackedEvents, + newEvents, + newEventParams + ) + ).toMatchInlineSnapshot(` + Array [ + Object { + "activeCount": 2, + "event": Object { + "kibana.alert.status": "active", + }, + "flappingHistory": Array [], + "pendingRecoveredCount": 0, + }, + Object { + "activeCount": 1, + "event": Object { + "kibana.alert.status": "active", + }, + "flappingHistory": Array [ + true, + true, + ], + "pendingRecoveredCount": 0, + }, + ] + `); + }); + + test('should reset activeCount for all recovered alerts', () => { + const trackedEvents = cloneDeep([alert1, alert2]); + expect( + getAlertsForNotification(DEFAULT_FLAPPING_SETTINGS, 0, trackedEvents, [], newEventParams) + ).toMatchInlineSnapshot(` + Array [ + Object { + "activeCount": 0, + "event": Object { + "kibana.alert.status": "recovered", + }, + "flapping": true, + "pendingRecoveredCount": 0, + }, + Object { + "activeCount": 0, + "event": Object { + "kibana.alert.status": "recovered", + }, + "flapping": false, + }, + ] + `); + }); + + test('should not return active alerts if the activeCount is less than the rule alertDelay', () => { + const trackedEvents = cloneDeep([alert4]); + const newEvents = cloneDeep([alert5]); + expect( + getAlertsForNotification( + DEFAULT_FLAPPING_SETTINGS, + 5, + trackedEvents, + newEvents, + newEventParams + ) + ).toMatchInlineSnapshot(`Array []`); + }); + + test('should update active alert to look like a new alert if the activeCount is equal to the rule alertDelay', () => { + const trackedEvents = cloneDeep([alert5]); + expect( + getAlertsForNotification(DEFAULT_FLAPPING_SETTINGS, 2, trackedEvents, [], newEventParams) + ).toMatchInlineSnapshot(` + Array [ + Object { + "activeCount": 2, + "event": Object { + "event.action": "open", + "kibana.alert.duration.us": 0, + "kibana.alert.maintenance_window_ids": Array [ + "maintenance-window-id", + ], + "kibana.alert.start": "timestamp", + "kibana.alert.status": "active", + "kibana.alert.time_range": Object { + "gte": "timestamp", + }, + }, + "flappingHistory": Array [], + "pendingRecoveredCount": 0, + }, + ] + `); + }); }); diff --git a/x-pack/plugins/rule_registry/server/utils/get_alerts_for_notification.ts b/x-pack/plugins/rule_registry/server/utils/get_alerts_for_notification.ts index 878db2a918022..5ec0e5b835eec 100644 --- a/x-pack/plugins/rule_registry/server/utils/get_alerts_for_notification.ts +++ b/x-pack/plugins/rule_registry/server/utils/get_alerts_for_notification.ts @@ -11,32 +11,68 @@ import { ALERT_STATUS, ALERT_STATUS_ACTIVE, ALERT_STATUS_RECOVERED, + ALERT_START, + ALERT_DURATION, EVENT_ACTION, + ALERT_TIME_RANGE, + ALERT_MAINTENANCE_WINDOW_IDS, } from '@kbn/rule-data-utils'; export function getAlertsForNotification( flappingSettings: RulesSettingsFlappingProperties, - trackedEventsToIndex: any[] + alertDelay: number, + trackedEventsToIndex: any[], + newEventsToIndex: any[], + newEventParams: { + // values used to create a new event + maintenanceWindowIds?: string[]; + timestamp: string; + } ) { - return trackedEventsToIndex.map((trackedEvent) => { - if (!flappingSettings.enabled || trackedEvent.event[ALERT_STATUS] === ALERT_STATUS_ACTIVE) { + const events: any[] = []; + for (const trackedEvent of [...newEventsToIndex, ...trackedEventsToIndex]) { + if (trackedEvent.event[ALERT_STATUS] === ALERT_STATUS_ACTIVE) { + const count = trackedEvent.activeCount || 0; + trackedEvent.activeCount = count + 1; trackedEvent.pendingRecoveredCount = 0; - } else if ( - flappingSettings.enabled && - trackedEvent.event[ALERT_STATUS] === ALERT_STATUS_RECOVERED - ) { - if (trackedEvent.flapping) { - const count = trackedEvent.pendingRecoveredCount || 0; - trackedEvent.pendingRecoveredCount = count + 1; - if (trackedEvent.pendingRecoveredCount < flappingSettings.statusChangeThreshold) { - trackedEvent.event[ALERT_STATUS] = ALERT_STATUS_ACTIVE; - trackedEvent.event[EVENT_ACTION] = 'active'; - delete trackedEvent.event[ALERT_END]; - } else { - trackedEvent.pendingRecoveredCount = 0; + // do not index the event if the number of consecutive + // active alerts is less than the rule alertDelay threshold + if (trackedEvent.activeCount < alertDelay) { + // remove from array of events to index + continue; + } else { + const { timestamp, maintenanceWindowIds } = newEventParams; + // if the active count is equal to the alertDelay it is considered a new event + if (trackedEvent.activeCount === alertDelay) { + // update the event to look like a new event + trackedEvent.event[ALERT_DURATION] = 0; + trackedEvent.event[ALERT_START] = timestamp; + trackedEvent.event[ALERT_TIME_RANGE] = { gte: timestamp }; + trackedEvent.event[EVENT_ACTION] = 'open'; + if (maintenanceWindowIds?.length) { + trackedEvent.event[ALERT_MAINTENANCE_WINDOW_IDS] = maintenanceWindowIds; + } } } + } else if (trackedEvent.event[ALERT_STATUS] === ALERT_STATUS_RECOVERED) { + trackedEvent.activeCount = 0; + if (flappingSettings.enabled) { + if (trackedEvent.flapping) { + const count = trackedEvent.pendingRecoveredCount || 0; + trackedEvent.pendingRecoveredCount = count + 1; + if (trackedEvent.pendingRecoveredCount < flappingSettings.statusChangeThreshold) { + trackedEvent.event[ALERT_STATUS] = ALERT_STATUS_ACTIVE; + trackedEvent.event[EVENT_ACTION] = 'active'; + delete trackedEvent.event[ALERT_END]; + } else { + trackedEvent.pendingRecoveredCount = 0; + } + } + } else { + trackedEvent.pendingRecoveredCount = 0; + } } - return trackedEvent; - }); + events.push(trackedEvent); + } + return events; } 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 index 52467f168e641..84685779186d9 100644 --- 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 @@ -49,6 +49,7 @@ describe('getUpdatedFlappingHistory', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, }, trackedAlertsRecovered: {}, @@ -81,6 +82,7 @@ describe('getUpdatedFlappingHistory', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, }, trackedAlerts: {}, @@ -115,6 +117,7 @@ describe('getUpdatedFlappingHistory', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, }, trackedAlertsRecovered: {}, @@ -150,6 +153,7 @@ describe('getUpdatedFlappingHistory', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, }, }; @@ -184,6 +188,7 @@ describe('getUpdatedFlappingHistory', () => { flappingHistory: [], flapping: false, pendingRecoveredCount: 0, + activeCount: 0, }, }, }; diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/integration_tests/pdfmaker.test.ts b/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/integration_tests/pdfmaker.test.ts index 2243f68b7ad71..9643a3bbcd4a1 100644 --- a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/integration_tests/pdfmaker.test.ts +++ b/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/integration_tests/pdfmaker.test.ts @@ -33,6 +33,7 @@ describe('PdfMaker', () => { branch: 'screenshot-test', buildNum: 567891011, buildSha: 'screenshot-dfdfed0a', + buildShaShort: 'scr-dfdfed0a', dist: false, version: '1000.0.0', buildDate: new Date('2023-05-15T23:12:09.000Z'), diff --git a/x-pack/plugins/screenshotting/server/screenshots/index.test.ts b/x-pack/plugins/screenshotting/server/screenshots/index.test.ts index 4a177a94a147b..3c6540f287706 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/index.test.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/index.test.ts @@ -56,6 +56,7 @@ describe('Screenshot Observable Pipeline', () => { branch: 'screenshot-test', buildNum: 567891011, buildSha: 'screenshot-dfdfed0a', + buildShaShort: 'scrn-dfdfed0a', dist: false, version: '5000.0.0', buildDate: new Date('2023-05-15T23:12:09.000Z'), diff --git a/x-pack/plugins/security/server/__snapshots__/prompt_page.test.tsx.snap b/x-pack/plugins/security/server/__snapshots__/prompt_page.test.tsx.snap index f24357ae373fa..b6bb95e80744b 100644 --- a/x-pack/plugins/security/server/__snapshots__/prompt_page.test.tsx.snap +++ b/x-pack/plugins/security/server/__snapshots__/prompt_page.test.tsx.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PromptPage renders as expected with additional scripts 1`] = `"ElasticMockedFonts

Some Title

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

Some Title

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

Some Title

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

Some Title

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

We hit an authentication error

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

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

We hit an authentication error

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

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

We hit an authentication error

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

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

We hit an authentication error

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

"`; diff --git a/x-pack/plugins/security/server/authentication/authentication_service.test.ts b/x-pack/plugins/security/server/authentication/authentication_service.test.ts index a779d30891b86..3f985df49cef1 100644 --- a/x-pack/plugins/security/server/authentication/authentication_service.test.ts +++ b/x-pack/plugins/security/server/authentication/authentication_service.test.ts @@ -19,6 +19,7 @@ import type { ElasticsearchServiceSetup, HttpServiceSetup, HttpServiceStart, + IStaticAssets, KibanaRequest, Logger, LoggerFactory, @@ -63,7 +64,7 @@ describe('AuthenticationService', () => { elasticsearch: jest.Mocked; config: ConfigType; license: jest.Mocked; - buildNumber: number; + staticAssets: IStaticAssets; customBranding: jest.Mocked; }; let mockStartAuthenticationParams: { @@ -96,7 +97,7 @@ describe('AuthenticationService', () => { isTLSEnabled: false, }), license: licenseMock.create(), - buildNumber: 100500, + staticAssets: coreSetupMock.http.staticAssets, customBranding: customBrandingServiceMock.createSetupContract(), }; mockCanRedirectRequest.mockReturnValue(false); @@ -983,7 +984,7 @@ describe('AuthenticationService', () => { expect(mockRenderUnauthorizedPage).toHaveBeenCalledWith({ basePath: mockSetupAuthenticationParams.http.basePath, - buildNumber: 100500, + staticAssets: expect.any(Object), originalURL: '/mock-server-basepath/app/some', }); }); @@ -1015,7 +1016,7 @@ describe('AuthenticationService', () => { expect(mockRenderUnauthorizedPage).toHaveBeenCalledWith({ basePath: mockSetupAuthenticationParams.http.basePath, - buildNumber: 100500, + staticAssets: expect.any(Object), originalURL: '/mock-server-basepath/app/some', }); }); @@ -1050,7 +1051,7 @@ describe('AuthenticationService', () => { expect(mockRenderUnauthorizedPage).toHaveBeenCalledWith({ basePath: mockSetupAuthenticationParams.http.basePath, - buildNumber: 100500, + staticAssets: expect.any(Object), originalURL: '/mock-server-basepath/', }); }); diff --git a/x-pack/plugins/security/server/authentication/authentication_service.ts b/x-pack/plugins/security/server/authentication/authentication_service.ts index d6f955b8b4558..cbe4e5f965691 100644 --- a/x-pack/plugins/security/server/authentication/authentication_service.ts +++ b/x-pack/plugins/security/server/authentication/authentication_service.ts @@ -40,12 +40,14 @@ import type { Session } from '../session_management'; import type { UserProfileServiceStartInternal } from '../user_profile'; interface AuthenticationServiceSetupParams { - http: Pick; + http: Pick< + HttpServiceSetup, + 'basePath' | 'csp' | 'registerAuth' | 'registerOnPreResponse' | 'staticAssets' + >; customBranding: CustomBrandingSetup; elasticsearch: Pick; config: ConfigType; license: SecurityLicense; - buildNumber: number; } interface AuthenticationServiceStartParams { @@ -92,7 +94,6 @@ export class AuthenticationService { config, http, license, - buildNumber, elasticsearch, customBranding, }: AuthenticationServiceSetupParams) { @@ -204,8 +205,8 @@ export class AuthenticationService { }); return toolkit.render({ body: renderUnauthenticatedPage({ - buildNumber, basePath: http.basePath, + staticAssets: http.staticAssets, originalURL, customBranding: customBrandingValue, }), diff --git a/x-pack/plugins/security/server/authentication/unauthenticated_page.test.tsx b/x-pack/plugins/security/server/authentication/unauthenticated_page.test.tsx index d65c032911a03..0abd444f80365 100644 --- a/x-pack/plugins/security/server/authentication/unauthenticated_page.test.tsx +++ b/x-pack/plugins/security/server/authentication/unauthenticated_page.test.tsx @@ -26,7 +26,7 @@ describe('UnauthenticatedPage', () => { const body = renderToStaticMarkup( @@ -44,7 +44,7 @@ describe('UnauthenticatedPage', () => { const body = renderToStaticMarkup( diff --git a/x-pack/plugins/security/server/authentication/unauthenticated_page.tsx b/x-pack/plugins/security/server/authentication/unauthenticated_page.tsx index fce29bbe89bc3..694f8f16bba0d 100644 --- a/x-pack/plugins/security/server/authentication/unauthenticated_page.tsx +++ b/x-pack/plugins/security/server/authentication/unauthenticated_page.tsx @@ -11,6 +11,7 @@ import { renderToStaticMarkup } from 'react-dom/server'; import type { IBasePath } from '@kbn/core/server'; import type { CustomBranding } from '@kbn/core-custom-branding-common'; +import type { IStaticAssets } from '@kbn/core-http-server'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -18,16 +19,21 @@ import { PromptPage } from '../prompt_page'; interface Props { originalURL: string; - buildNumber: number; basePath: IBasePath; + staticAssets: IStaticAssets; customBranding: CustomBranding; } -export function UnauthenticatedPage({ basePath, originalURL, buildNumber, customBranding }: Props) { +export function UnauthenticatedPage({ + basePath, + originalURL, + staticAssets, + customBranding, +}: Props) { return ( ElasticMockedFonts

You do not have permission to access the requested page

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

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

You do not have permission to access the requested page

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

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

You do not have permission to access the requested page

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

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

You do not have permission to access the requested page

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

"`; diff --git a/x-pack/plugins/security/server/authorization/authorization_service.test.ts b/x-pack/plugins/security/server/authorization/authorization_service.test.ts index a052d67532480..ddc5e26903c2b 100644 --- a/x-pack/plugins/security/server/authorization/authorization_service.test.ts +++ b/x-pack/plugins/security/server/authorization/authorization_service.test.ts @@ -76,7 +76,6 @@ it(`#setup returns exposed services`, () => { loggers: loggingSystemMock.create(), kibanaIndexName, packageVersion: 'some-version', - buildNumber: 42, features: mockFeaturesSetup, getSpacesService: mockGetSpacesService, getCurrentUser: jest.fn(), @@ -138,7 +137,6 @@ describe('#start', () => { loggers: loggingSystemMock.create(), kibanaIndexName, packageVersion: 'some-version', - buildNumber: 42, features: featuresPluginMock.createSetup(), getSpacesService: jest .fn() @@ -211,7 +209,6 @@ it('#stop unsubscribes from license and ES updates.', async () => { loggers: loggingSystemMock.create(), kibanaIndexName, packageVersion: 'some-version', - buildNumber: 42, features: featuresPluginMock.createSetup(), getSpacesService: jest .fn() diff --git a/x-pack/plugins/security/server/authorization/authorization_service.tsx b/x-pack/plugins/security/server/authorization/authorization_service.tsx index 16f2ed3b446e1..795016874dc97 100644 --- a/x-pack/plugins/security/server/authorization/authorization_service.tsx +++ b/x-pack/plugins/security/server/authorization/authorization_service.tsx @@ -57,7 +57,6 @@ export { Actions } from './actions'; interface AuthorizationServiceSetupParams { packageVersion: string; - buildNumber: number; http: HttpServiceSetup; capabilities: CapabilitiesSetup; getClusterClient: () => Promise; @@ -100,7 +99,6 @@ export class AuthorizationService { http, capabilities, packageVersion, - buildNumber, getClusterClient, license, loggers, @@ -179,7 +177,7 @@ export class AuthorizationService { const next = `${http.basePath.get(request)}${request.url.pathname}${request.url.search}`; const body = renderToString( { const body = renderToStaticMarkup( @@ -44,7 +44,7 @@ describe('ResetSessionPage', () => { const body = renderToStaticMarkup( diff --git a/x-pack/plugins/security/server/authorization/reset_session_page.tsx b/x-pack/plugins/security/server/authorization/reset_session_page.tsx index 85c78ddfcbaec..27af66a8a4048 100644 --- a/x-pack/plugins/security/server/authorization/reset_session_page.tsx +++ b/x-pack/plugins/security/server/authorization/reset_session_page.tsx @@ -10,6 +10,7 @@ import React from 'react'; import type { IBasePath } from '@kbn/core/server'; import type { CustomBranding } from '@kbn/core-custom-branding-common'; +import type { IStaticAssets } from '@kbn/core-http-server'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -22,18 +23,18 @@ import { PromptPage } from '../prompt_page'; */ export function ResetSessionPage({ logoutUrl, - buildNumber, + staticAssets, basePath, customBranding, }: { logoutUrl: string; - buildNumber: number; + staticAssets: IStaticAssets; basePath: IBasePath; customBranding: CustomBranding; }) { return ( spaces?.spacesService, features, getCurrentUser: (request) => this.getAuthentication().getCurrentUser(request), diff --git a/x-pack/plugins/security/server/prompt_page.test.tsx b/x-pack/plugins/security/server/prompt_page.test.tsx index 268a7ac640e71..754584284035a 100644 --- a/x-pack/plugins/security/server/prompt_page.test.tsx +++ b/x-pack/plugins/security/server/prompt_page.test.tsx @@ -25,7 +25,7 @@ describe('PromptPage', () => { const body = renderToStaticMarkup( Some Body
} @@ -45,7 +45,7 @@ describe('PromptPage', () => { const body = renderToStaticMarkup( ; -export const EndpointParams = z.object({ +export type DefaultParams = z.infer; +export const DefaultParams = z.object({ command: z.literal('isolate'), comment: z.string().optional(), }); +export type ProcessesParams = z.infer; +export const ProcessesParams = z.object({ + command: z.enum(['kill-process', 'suspend-process']), + comment: z.string().optional(), + config: z.object({ + /** + * Field to use instead of process.pid + */ + field: z.string(), + /** + * Whether to overwrite field with process.pid + */ + overwrite: z.boolean().optional().default(true), + }), +}); + export type EndpointResponseAction = z.infer; export const EndpointResponseAction = z.object({ action_type_id: z.literal('.endpoint'), - params: EndpointParams, + params: z.union([DefaultParams, ProcessesParams]), }); export type RuleResponseEndpointAction = z.infer; export const RuleResponseEndpointAction = z.object({ actionTypeId: z.literal('.endpoint'), - params: EndpointParams, + params: z.union([DefaultParams, ProcessesParams]), }); export type ResponseAction = z.infer; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.schema.yaml index 3e4b37e115add..751a1efee8fa8 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.schema.yaml @@ -2,7 +2,7 @@ openapi: 3.0.0 info: title: Response Actions Schema version: 'not applicable' -paths: {} +paths: { } components: x-codegen-enabled: true schemas: @@ -113,7 +113,7 @@ components: - actionTypeId - params - EndpointParams: + DefaultParams: type: object properties: command: @@ -125,6 +125,32 @@ components: required: - command + ProcessesParams: + type: object + properties: + command: + type: string + enum: + - kill-process + - suspend-process + comment: + type: string + config: + required: + - field + type: object + properties: + field: + type: string + description: Field to use instead of process.pid + overwrite: + type: boolean + description: Whether to overwrite field with process.pid + default: true + required: + - command + - config + EndpointResponseAction: type: object properties: @@ -133,7 +159,9 @@ components: enum: - .endpoint params: - $ref: '#/components/schemas/EndpointParams' + oneOf: + - $ref: '#/components/schemas/DefaultParams' + - $ref: '#/components/schemas/ProcessesParams' required: - action_type_id - params @@ -147,7 +175,9 @@ components: enum: - .endpoint params: - $ref: '#/components/schemas/EndpointParams' + oneOf: + - $ref: '#/components/schemas/DefaultParams' + - $ref: '#/components/schemas/ProcessesParams' required: - actionTypeId - params diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/response_actions.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/response_actions.ts index 8f176a9908041..e4164c6d2bb04 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/response_actions.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/response_actions.ts @@ -6,7 +6,6 @@ */ import { arrayQueries, ecsMapping } from '@kbn/osquery-io-ts-types'; import * as t from 'io-ts'; -import { ENABLED_AUTOMATED_RESPONSE_ACTION_COMMANDS } from '../../../../endpoint/service/response_actions/constants'; // to enable using RESPONSE_ACTION_API_COMMANDS_NAMES as a type function keyObject(arr: T): { [K in T[number]]: null } { @@ -15,7 +14,9 @@ function keyObject(arr: T): { [K in T[number]]: nul export type EndpointParams = t.TypeOf; export const EndpointParams = t.type({ - command: t.keyof(keyObject(ENABLED_AUTOMATED_RESPONSE_ACTION_COMMANDS)), + // TODO: TC- change these when we go GA with automated process actions + command: t.keyof(keyObject(['isolate', 'kill-process', 'suspend-process'])), + // command: t.keyof(keyObject(ENABLED_AUTOMATED_RESPONSE_ACTION_COMMANDS)), comment: t.union([t.string, t.undefined]), }); diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 4acadbd8e39ea..cf9e5ed8ba61f 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -8,6 +8,7 @@ import { RuleNotifyWhen } from '@kbn/alerting-plugin/common'; import type { AddOptionsListControlProps } from '@kbn/controls-plugin/public'; import * as i18n from './translations'; + export { SecurityPageName } from '@kbn/security-solution-navigation'; /** @@ -338,12 +339,12 @@ export const ALERTS_AS_DATA_FIND_URL = `${ALERTS_AS_DATA_URL}/find` as const; export const UNAUTHENTICATED_USER = 'Unauthenticated' as const; /** - Licensing requirements + Licensing requirements */ export const MINIMUM_ML_LICENSE = 'platinum' as const; /** - Machine Learning constants + Machine Learning constants */ export const ML_GROUP_ID = 'security' as const; export const LEGACY_ML_GROUP_ID = 'siem' as const; @@ -519,3 +520,8 @@ export const DEFAULT_ALERT_TAGS_VALUE = [ * Max length for the comments within security solution */ export const MAX_COMMENT_LENGTH = 30000 as const; + +/** + * Cases external attachment IDs + */ +export const CASE_ATTACHMENT_ENDPOINT_TYPE_ID = 'endpoint' as const; diff --git a/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_rule_alert_generator.ts b/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_rule_alert_generator.ts index 1b74ae55f6289..e89cb996f261f 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_rule_alert_generator.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_rule_alert_generator.ts @@ -287,6 +287,28 @@ export class EndpointRuleAlertGenerator extends BaseDataGenerator { comment: 'test', }, }, + { + params: { + command: 'suspend-process', + comment: 'Suspend host', + config: { + field: 'entity_id', + overwrite: false, + }, + }, + action_type_id: '.endpoint', + }, + { + params: { + command: 'kill-process', + comment: 'Kill host', + config: { + field: '', + overwrite: true, + }, + }, + action_type_id: '.endpoint', + }, ], rule_id: ELASTIC_SECURITY_RULE_ID, rule_name_override: 'message', diff --git a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_fleet_actions.ts b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_fleet_actions.ts index f94f148765ba5..5eb2cebcc3f8e 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_fleet_actions.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_fleet_actions.ts @@ -9,6 +9,7 @@ import type { Client } from '@elastic/elasticsearch'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { AGENT_ACTIONS_INDEX, AGENT_ACTIONS_RESULTS_INDEX } from '@kbn/fleet-plugin/common'; import type { BulkRequest } from '@elastic/elasticsearch/lib/api/types'; +import type { ResponseActionsApiCommandNames } from '../service/response_actions/constants'; import { EndpointError } from '../errors'; import { usageTracker } from './usage_tracker'; import type { @@ -117,6 +118,15 @@ interface BuildIEndpointAndFleetActionsBulkOperationsResponse operations: Required['operations']; } +const getAutomatedActionsSample = (): Array<{ + command: ResponseActionsApiCommandNames; + config?: { overwrite: boolean }; +}> => [ + { command: 'isolate' }, + { command: 'suspend-process', config: { overwrite: true } }, + { command: 'kill-process', config: { overwrite: true } }, +]; + export const buildIEndpointAndFleetActionsBulkOperations = ({ endpoints, count = 1, @@ -138,13 +148,14 @@ export const buildIEndpointAndFleetActionsBulkOperations = ({ for (const endpoint of endpoints) { const agentId = endpoint.elastic.agent.id; + const automatedActions = getAutomatedActionsSample(); for (let i = 0; i < count; i++) { // start with endpoint action const logsEndpointAction: LogsEndpointAction = endpointActionGenerator.generate({ EndpointActions: { data: { comment: 'data generator: this host is bad', - ...(alertIds ? { command: 'isolate' } : {}), + ...(alertIds ? automatedActions[i] : {}), }, }, }); diff --git a/x-pack/plugins/security_solution/common/endpoint/errors.ts b/x-pack/plugins/security_solution/common/endpoint/errors.ts index 495afaa126e2b..2cd7fd931583b 100644 --- a/x-pack/plugins/security_solution/common/endpoint/errors.ts +++ b/x-pack/plugins/security_solution/common/endpoint/errors.ts @@ -14,5 +14,9 @@ export class EndpointError extends Error { super(message); // For debugging - capture name of subclasses this.name = this.constructor.name; + + if (meta instanceof Error) { + this.stack += `\n----- original error -----\n${meta.stack}`; + } } } diff --git a/x-pack/plugins/security_solution/common/endpoint/service/response_actions/constants.ts b/x-pack/plugins/security_solution/common/endpoint/service/response_actions/constants.ts index 6f8daab94e186..e8de7fc1c9122 100644 --- a/x-pack/plugins/security_solution/common/endpoint/service/response_actions/constants.ts +++ b/x-pack/plugins/security_solution/common/endpoint/service/response_actions/constants.ts @@ -31,7 +31,12 @@ export const RESPONSE_ACTION_API_COMMANDS_NAMES = [ export type ResponseActionsApiCommandNames = typeof RESPONSE_ACTION_API_COMMANDS_NAMES[number]; -export const ENABLED_AUTOMATED_RESPONSE_ACTION_COMMANDS = ['isolate'] as const; +export const ENABLED_AUTOMATED_RESPONSE_ACTION_COMMANDS: ResponseActionsApiCommandNames[] = [ + 'isolate', + // TODO: TC- Uncomment these when we go GA with automated process actions + // 'kill-process', + // 'suspend-process' +]; export type EnabledAutomatedResponseActionsCommands = typeof ENABLED_AUTOMATED_RESPONSE_ACTION_COMMANDS[number]; diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index bed4a958b16b4..e7c5cf104b7c7 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -70,6 +70,11 @@ export const allowedExperimentalValues = Object.freeze({ */ responseActionUploadEnabled: true, + /* + * Enables Automated Endpoint Process actions + */ + automatedProcessActionsEnabled: false, + /** * Enables the ability to send Response actions to SentinelOne */ diff --git a/x-pack/plugins/security_solution/public/cases/attachments/external_reference.tsx b/x-pack/plugins/security_solution/public/cases/attachments/external_reference.tsx new file mode 100644 index 0000000000000..3a96cfec14897 --- /dev/null +++ b/x-pack/plugins/security_solution/public/cases/attachments/external_reference.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 React from 'react'; +import { EuiAvatar } from '@elastic/eui'; +import type { ExternalReferenceAttachmentType } from '@kbn/cases-plugin/public/client/attachment_framework/types'; +import { getLazyExternalChildrenContent } from './lazy_external_reference_children_content'; +import { CASE_ATTACHMENT_ENDPOINT_TYPE_ID } from '../../../common/constants'; +import { getLazyExternalEventContent } from './lazy_external_reference_content'; +import type { IExternalReferenceMetaDataProps } from './types'; + +export const getExternalReferenceAttachmentEndpointRegular = + (): ExternalReferenceAttachmentType => ({ + id: CASE_ATTACHMENT_ENDPOINT_TYPE_ID, + displayName: 'Endpoint', + // @ts-expect-error: TS2322 figure out types for children lazyExotic + getAttachmentViewObject: (props: IExternalReferenceMetaDataProps) => { + const iconType = props.externalReferenceMetadata?.command === 'isolate' ? 'lock' : 'lockOpen'; + return { + type: 'regular', + event: getLazyExternalEventContent(props), + timelineAvatar: ( + + ), + children: getLazyExternalChildrenContent, + }; + }, + }); diff --git a/x-pack/plugins/security_solution/public/cases/attachments/external_reference_children.test.tsx b/x-pack/plugins/security_solution/public/cases/attachments/external_reference_children.test.tsx new file mode 100644 index 0000000000000..e542e74a1f702 --- /dev/null +++ b/x-pack/plugins/security_solution/public/cases/attachments/external_reference_children.test.tsx @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import AttachmentContentChildren from './external_reference_children'; + +describe('AttachmentContentChildren', () => { + const defaultProps = { + command: 'isolate', + comment: 'Test comment', + targets: [ + { + endpointId: 'endpoint-1', + hostname: 'host-1', + agentType: 'endpoint' as const, + }, + ], + }; + + it('renders markdown content when comment exists', () => { + const props = { + externalReferenceMetadata: { + ...defaultProps, + }, + }; + const { getByText } = render(); + expect(getByText('Test comment')).toBeInTheDocument(); + }); + + it('does not render when comment is empty', () => { + const props = { + externalReferenceMetadata: { + ...defaultProps, + comment: '', + }, + }; + const { container } = render(); + expect(container.firstChild).toBeNull(); + }); + + it('does not render when comment is only whitespace', () => { + const props = { + externalReferenceMetadata: { + ...defaultProps, + comment: ' ', + }, + }; + + const { container } = render(); + expect(container.firstChild).toBeNull(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/cases/attachments/external_reference_children.tsx b/x-pack/plugins/security_solution/public/cases/attachments/external_reference_children.tsx new file mode 100644 index 0000000000000..57e03fd66aa39 --- /dev/null +++ b/x-pack/plugins/security_solution/public/cases/attachments/external_reference_children.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 React from 'react'; + +import styled from 'styled-components'; +import { EuiMarkdownFormat } from '@elastic/eui'; +import type { IExternalReferenceMetaDataProps } from './types'; + +export const ContentWrapper = styled.div` + padding: ${({ theme }) => `${theme.eui?.euiSizeM} ${theme.eui?.euiSizeL}`}; + text-overflow: ellipsis; + word-break: break-word; + display: -webkit-box; + -webkit-box-orient: vertical; +`; + +const AttachmentContentChildren = ({ + externalReferenceMetadata: { comment }, +}: IExternalReferenceMetaDataProps) => { + return comment.trim().length > 0 ? ( + + {comment} + + ) : null; +}; +// eslint-disable-next-line import/no-default-export +export { AttachmentContentChildren as default }; diff --git a/x-pack/plugins/security_solution/public/cases/attachments/external_reference_event.test.tsx b/x-pack/plugins/security_solution/public/cases/attachments/external_reference_event.test.tsx new file mode 100644 index 0000000000000..4e5c504cd50c9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/cases/attachments/external_reference_event.test.tsx @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; + +import AttachmentContentEvent from './external_reference_event'; +import { useNavigation } from '@kbn/security-solution-navigation/src/navigation'; + +jest.mock('@kbn/security-solution-navigation/src/navigation', () => { + return { + useNavigation: jest.fn(), + }; +}); + +describe('AttachmentContentEvent', () => { + const mockNavigateTo = jest.fn(); + + const mockUseNavigation = useNavigation as jest.Mocked; + (mockUseNavigation as jest.Mock).mockReturnValue({ + getAppUrl: jest.fn(), + navigateTo: mockNavigateTo, + }); + + const defaultProps = { + externalReferenceMetadata: { + command: 'isolate', + comment: 'test comment', + targets: [ + { + endpointId: 'endpoint-1', + hostname: 'host-1', + agentType: 'endpoint' as const, + }, + ], + }, + }; + + it('renders the expected text based on the command', () => { + const { getByText, getByTestId, rerender } = render( + + ); + + expect(getByText('submitted isolate request on host')).toBeInTheDocument(); + expect(getByTestId('actions-link-endpoint-1')).toHaveTextContent('host-1'); + + rerender( + + ); + + expect(getByText('submitted release request on host')).toBeInTheDocument(); + expect(getByTestId('actions-link-endpoint-1')).toHaveTextContent('host-1'); + }); + + it('navigates on link click', () => { + const { getByTestId } = render(); + + fireEvent.click(getByTestId('actions-link-endpoint-1')); + + expect(mockNavigateTo).toHaveBeenCalled(); + }); + + it('builds endpoint details URL correctly', () => { + const mockGetAppUrl = jest.fn().mockReturnValue('http://app.url'); + (mockUseNavigation as jest.Mock).mockReturnValue({ + getAppUrl: mockGetAppUrl, + }); + + render(); + + expect(mockGetAppUrl).toHaveBeenNthCalledWith(1, { + path: '/administration/endpoints?selected_endpoint=endpoint-1&show=activity_log', + }); + expect(mockGetAppUrl).toHaveBeenNthCalledWith(2, { + path: '/hosts/name/host-1', + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/cases/attachments/external_reference_event.tsx b/x-pack/plugins/security_solution/public/cases/attachments/external_reference_event.tsx new file mode 100644 index 0000000000000..1cb1910a9b1b9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/cases/attachments/external_reference_event.tsx @@ -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 { EuiLink } from '@elastic/eui'; +import { useNavigation } from '@kbn/security-solution-navigation/src/navigation'; +import React, { useCallback, useMemo } from 'react'; + +import { ISOLATED_HOST, RELEASED_HOST, OTHER_ENDPOINTS } from '../pages/translations'; +import type { IExternalReferenceMetaDataProps } from './types'; +import { getEndpointDetailsPath } from '../../management/common/routing'; + +const AttachmentContentEvent = ({ + externalReferenceMetadata: { command, targets }, +}: IExternalReferenceMetaDataProps) => { + const { getAppUrl, navigateTo } = useNavigation(); + + const endpointDetailsHref = getAppUrl({ + path: getEndpointDetailsPath({ + name: 'endpointActivityLog', + selected_endpoint: targets[0].endpointId, + }), + }); + const hostsDetailsHref = getAppUrl({ + path: `/hosts/name/${targets[0].hostname}`, + }); + + const actionText = useMemo(() => { + return command === 'isolate' ? `${ISOLATED_HOST} ` : `${RELEASED_HOST} `; + }, [command]); + + const linkHref = useMemo( + () => (targets[0].agentType === 'endpoint' ? endpointDetailsHref : hostsDetailsHref), + [endpointDetailsHref, hostsDetailsHref, targets] + ); + + const onLinkClick = useCallback( + (ev: React.MouseEvent) => { + ev.preventDefault(); + return navigateTo({ url: linkHref }); + }, + [navigateTo, linkHref] + ); + + return ( + <> + {actionText} + {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} + + {targets[0].hostname} + + {targets.length > 1 && OTHER_ENDPOINTS(targets.length - 1)} + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { AttachmentContentEvent as default }; diff --git a/x-pack/plugins/security_solution/public/cases/attachments/lazy_external_reference_children_content.tsx b/x-pack/plugins/security_solution/public/cases/attachments/lazy_external_reference_children_content.tsx new file mode 100644 index 0000000000000..b88eeddf2b6bc --- /dev/null +++ b/x-pack/plugins/security_solution/public/cases/attachments/lazy_external_reference_children_content.tsx @@ -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 React, { lazy, Suspense } from 'react'; +import type { IExternalReferenceMetaDataProps } from './types'; + +const AttachmentContent = lazy(() => import('./external_reference_children')); + +export const getLazyExternalChildrenContent = (props: IExternalReferenceMetaDataProps) => { + return ( + + + + ); +}; diff --git a/x-pack/plugins/security_solution/public/cases/attachments/lazy_external_reference_content.tsx b/x-pack/plugins/security_solution/public/cases/attachments/lazy_external_reference_content.tsx new file mode 100644 index 0000000000000..cbcf2dade0a8e --- /dev/null +++ b/x-pack/plugins/security_solution/public/cases/attachments/lazy_external_reference_content.tsx @@ -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 React, { lazy, Suspense } from 'react'; +import type { IExternalReferenceMetaDataProps } from './types'; + +const AttachmentContent = lazy(() => import('./external_reference_event')); + +export const getLazyExternalEventContent = (props: IExternalReferenceMetaDataProps) => { + return ( + + + + ); +}; diff --git a/x-pack/plugins/security_solution/public/cases/attachments/types.ts b/x-pack/plugins/security_solution/public/cases/attachments/types.ts new file mode 100644 index 0000000000000..341b21a7576e4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/cases/attachments/types.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ResponseActionAgentType } from '../../../common/endpoint/service/response_actions/constants'; + +export interface IExternalReferenceMetaDataProps { + externalReferenceMetadata: { + comment: ExternalReferenceCommentType; + command: ExternalReferenceCommandType; + targets: ExternalReferenceTargetsType; + }; +} + +type ExternalReferenceTargetsType = Array<{ + endpointId: string; + hostname: string; + agentType: ResponseActionAgentType; +}>; +type ExternalReferenceCommentType = string; +type ExternalReferenceCommandType = string; diff --git a/x-pack/plugins/security_solution/public/cases/pages/translations.ts b/x-pack/plugins/security_solution/public/cases/pages/translations.ts index d6f33d2a90ffa..776b7acc11898 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/translations.ts +++ b/x-pack/plugins/security_solution/public/cases/pages/translations.ts @@ -10,3 +10,17 @@ import { i18n } from '@kbn/i18n'; export const PAGE_TITLE = i18n.translate('xpack.securitySolution.cases.pageTitle', { defaultMessage: 'Cases', }); + +export const ISOLATED_HOST = i18n.translate('xpack.securitySolution.caseView.isolatedHost', { + defaultMessage: 'submitted isolate request on host', +}); + +export const RELEASED_HOST = i18n.translate('xpack.securitySolution.caseView.releasedHost', { + defaultMessage: 'submitted release request on host', +}); + +export const OTHER_ENDPOINTS = (endpoints: number): string => + i18n.translate('xpack.securitySolution.caseView.otherEndpoints', { + values: { endpoints }, + defaultMessage: ` and {endpoints} {endpoints, plural, =1 {other} other {others}}`, + }); diff --git a/x-pack/plugins/security_solution/public/common/translations.ts b/x-pack/plugins/security_solution/public/common/translations.ts index 551e977ec9861..676546b63b224 100644 --- a/x-pack/plugins/security_solution/public/common/translations.ts +++ b/x-pack/plugins/security_solution/public/common/translations.ts @@ -77,7 +77,7 @@ export const UNSAVED_TIMELINE_SAVE_PROMPT_TITLE = i18n.translate( export const getAgentTypeName = (agentType: ResponseActionAgentType) => { switch (agentType) { case 'endpoint': - return 'Endpoint'; + return 'Elastic Defend'; case 'sentinel_one': return 'SentinelOne'; default: diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/action_type_field.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/action_type_field.tsx index fa40754f222c4..60857e4b5c49a 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/action_type_field.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/action_type_field.tsx @@ -13,6 +13,7 @@ import { SuperSelectField } from '@kbn/es-ui-shared-plugin/static/forms/componen import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiLink } from '@elastic/eui'; +import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; import { getRbacControl } from '../../../../common/endpoint/service/response_actions/utils'; import { useKibana } from '../../../common/lib/kibana'; import { CHOOSE_FROM_THE_LIST, LEARN_MORE } from './translations'; @@ -44,14 +45,29 @@ const ActionTypeFieldComponent = ({ }, } = useKibana().services; + const automatedProcessActionsEnabled = useIsExperimentalFeatureEnabled( + 'automatedProcessActionsEnabled' + ); + + const enabledActions = useMemo( + () => + [ + ...ENABLED_AUTOMATED_RESPONSE_ACTION_COMMANDS, + ...(automatedProcessActionsEnabled ? ['kill-process', 'suspend-process'] : []), + ] as ['isolate', 'kill-process', 'suspend-process'], + [automatedProcessActionsEnabled] + ); + const fieldOptions = useMemo( () => - ENABLED_AUTOMATED_RESPONSE_ACTION_COMMANDS.map((name) => { + enabledActions.map((name) => { const missingRbac = !getRbacControl({ commandName: RESPONSE_ACTION_API_COMMAND_TO_CONSOLE_COMMAND_MAP[name], privileges: endpointPrivileges, }); - const commandAlreadyExists = map(data.responseActions, 'params.command').includes(name); + const currentActions = map(data.responseActions, 'params.command'); + // we enable just one instance of each action + const commandAlreadyExists = currentActions.includes(name); const isDisabled = commandAlreadyExists || missingRbac; return { @@ -62,7 +78,7 @@ const ActionTypeFieldComponent = ({ 'data-test-subj': `command-type-${name}`, }; }), - [data.responseActions, endpointPrivileges] + [data.responseActions, enabledActions, endpointPrivileges] ); return ( diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/callout.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/callout.tsx index 07dbb0c9f2cbf..3468743f5d9d7 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/callout.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/callout.tsx @@ -71,6 +71,31 @@ const EndpointActionCalloutComponent = ({ basePath, editDisabled }: EndpointCall ); } + if (currentCommand === 'kill-process' || currentCommand === 'suspend-process') { + return ( + <> + + + } + > + + + + + + + ); + } return <>; }; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/config_fields.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/config_fields.tsx new file mode 100644 index 0000000000000..e59fb52425d94 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/config_fields.tsx @@ -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 React from 'react'; +import { useFormData } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { EuiSpacer } from '@elastic/eui'; +import { get } from 'lodash'; +import { OverwriteField } from './overwrite_process_field'; +import { FieldNameField } from './field_name'; + +interface AdditionalConfigFieldProps { + basePath: string; + disabled: boolean; + readDefaultValueOnForm: boolean; +} + +export const ConfigFieldsComponent = ({ + basePath, + disabled, + readDefaultValueOnForm, +}: AdditionalConfigFieldProps) => { + const commandPath = `${basePath}.command`; + const overWritePath = `${basePath}.config.overwrite`; + const [data] = useFormData({ watch: [commandPath, overWritePath] }); + const currentCommand = get(data, commandPath); + const currentOverwrite = get(data, overWritePath); + + if (currentCommand === 'kill-process' || currentCommand === 'suspend-process') { + return ( + <> + + + + + + + ); + } + + return null; +}; + +export const ConfigFields = React.memo(ConfigFieldsComponent); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/endpoint_response_action.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/endpoint_response_action.tsx index 940cab8eb1b8b..84518ac097481 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/endpoint_response_action.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/endpoint_response_action.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { ConfigFields } from './config_fields'; import type { ArrayItem } from '../../../shared_imports'; import { CommentField } from './comment_field'; import { ActionTypeField } from './action_type_field'; @@ -29,6 +30,12 @@ export const EndpointResponseAction = React.memo((props: EndpointResponseActionP + + ({ + label: ecs.field, + value: ecs, +})); + +const SINGLE_SELECTION = Object.freeze({ asPlainText: true }); + +const FIELD_LABEL: string = 'Custom field name'; +const FieldNameFieldComponent = ({ + path, + disabled, + readDefaultValueOnForm, + isRequired, +}: FieldNameFieldProps) => { + const [data] = useFormData(); + const fieldValue = get(data, path); + const context = useFormContext(); + + const currentFieldNameField = context.getFields()[path]; + + useEffect(() => { + // hackish way to clear errors on this field - because we base this validation on the value of overwrite toggle + if (currentFieldNameField && !isRequired) { + currentFieldNameField?.clearErrors(); + } + }, [currentFieldNameField, isRequired]); + + const renderEntityIdNote = useMemo(() => { + const contains = fieldValue?.includes('entity_id'); + if (contains) { + return ( + + ); + } + return null; + }, [fieldValue]); + + const CONFIG = useMemo(() => { + return { + label: FIELD_LABEL, + helpText: renderEntityIdNote, + validations: [ + { + validator: ({ value }: { value: string }) => { + if (isRequired && value === '') { + return { + code: 'ERR_FIELD_MISSING', + path, + message: i18n.translate( + 'xpack.securitySolution.responseActions.endpoint.validations.fieldNameIsRequiredErrorMessage', + { + defaultMessage: + '{field} is a required field when process.pid toggle is turned off', + values: { field: FIELD_LABEL }, + } + ), + }; + } + }, + }, + ], + }; + }, [isRequired, path, renderEntityIdNote]); + + const optionsAsComboBoxOptions = useMemo(() => { + return ECSSchemaOptions.map(({ label }) => ({ + label, + value: label, + })); + }, []); + + return ( + <> + path={path} readDefaultValueOnForm={readDefaultValueOnForm} config={CONFIG}> + {(field) => { + const { value, setValue } = field; + const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field); + + const valueInList = !!optionsAsComboBoxOptions.find((option) => option.label === value); + return ( + + { + if (newValue.length === 0) { + // Don't allow clearing the type. One must always be selected + return; + } + setValue(newValue[0].label); + }} + data-test-subj="config-custom-field-name" + /> + + ); + }} + + + + ); +}; +export const FieldNameField = React.memo(FieldNameFieldComponent); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/overwrite_process_field.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/overwrite_process_field.tsx new file mode 100644 index 0000000000000..143bd78e339d7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/overwrite_process_field.tsx @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useMemo } from 'react'; +import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { ToggleField } from '@kbn/es-ui-shared-plugin/static/forms/components'; +import { i18n } from '@kbn/i18n'; + +interface OverwriteFieldProps { + path: string; + disabled: boolean; + readDefaultValueOnForm: boolean; +} + +const OverwriteFieldComponent = ({ + path, + disabled, + readDefaultValueOnForm, +}: OverwriteFieldProps) => { + const CONFIG = useMemo(() => { + return { + defaultValue: true, + label: i18n.translate('xpack.securitySolution.responseActions.endpoint.overwriteFieldLabel', { + defaultMessage: 'Use process.pid as process identifier', + }), + }; + }, []); + + return ( + + ); +}; + +export const OverwriteField = React.memo(OverwriteFieldComponent); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/utils.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/utils.tsx index 58f8dd2e3548e..a07fd84bf4430 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/utils.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/utils.tsx @@ -6,7 +6,7 @@ */ import type { ReactNode } from 'react'; import React from 'react'; -import { EuiText, EuiTitle, EuiSpacer, EuiToolTip } from '@elastic/eui'; +import { EuiText, EuiSpacer, EuiToolTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import type { EnabledAutomatedResponseActionsCommands } from '../../../../common/endpoint/service/response_actions/constants'; @@ -20,11 +20,11 @@ const EndpointActionTextComponent = ({ name, isDisabled }: EndpointActionTextPro const content = ( <> - - {title} - + + {title} + - {description} + {description} ); if (isDisabled) { @@ -62,6 +62,48 @@ const useGetCommandText = ( /> ), }; + case 'kill-process': + return { + title: ( + + ), + description: ( + + ), + tooltip: ( + + ), + }; + case 'suspend-process': + return { + title: ( + + ), + description: ( + + ), + tooltip: ( + + ), + }; default: return { title: '', diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/v.8.10.0_process.json b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/v.8.10.0_process.json new file mode 100644 index 0000000000000..986a60ba6d48f --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/v.8.10.0_process.json @@ -0,0 +1,122 @@ +[ + { + "field": "process.entity_id", + "description": "Unique identifier for the process." + }, + { + "field": "process.entry_leader.entity_id", + "description": "Unique identifier for the process." + }, + { + "field": "process.entry_leader.parent.entity_id", + "description": "Unique identifier for the process." + }, + { + "field": "process.entry_leader.parent.pid", + "description": "Process id." + }, + { + "field": "process.entry_leader.parent.session_leader.entity_id", + "description": "Unique identifier for the process." + }, + { + "field": "process.entry_leader.parent.session_leader.pid", + "description": "Process id." + }, + { + "field": "process.entry_leader.parent.session_leader.vpid", + "description": "Virtual process id." + }, + { + "field": "process.entry_leader.parent.vpid", + "description": "Virtual process id." + }, + { + "field": "process.entry_leader.pid", + "description": "Process id." + }, + { + "field": "process.entry_leader.vpid", + "description": "Virtual process id." + }, + { + "field": "process.group_leader.entity_id", + "description": "Unique identifier for the process." + }, + { + "field": "process.group_leader.pid", + "description": "Process id." + }, + { + "field": "process.group_leader.vpid", + "description": "Virtual process id." + }, + { + "field": "process.parent.entity_id", + "description": "Unique identifier for the process." + }, + { + "field": "process.parent.group_leader.entity_id", + "description": "Unique identifier for the process." + }, + { + "field": "process.parent.group_leader.pid", + "description": "Process id." + }, + { + "field": "process.parent.group_leader.vpid", + "description": "Virtual process id." + }, + { + "field": "process.parent.pid", + "description": "Process id." + }, + { + "field": "process.parent.vpid", + "description": "Virtual process id." + }, + { + "field": "process.pid", + "description": "Process id." + }, + { + "field": "process.session_leader.entity_id", + "description": "Unique identifier for the process." + }, + { + "field": "process.session_leader.parent.entity_id", + "description": "Unique identifier for the process." + }, + { + "field": "process.session_leader.parent.pid", + "description": "Process id." + }, + { + "field": "process.session_leader.parent.session_leader.entity_id", + "description": "Unique identifier for the process." + }, + { + "field": "process.session_leader.parent.session_leader.pid", + "description": "Process id." + }, + { + "field": "process.session_leader.parent.session_leader.vpid", + "description": "Virtual process id." + }, + { + "field": "process.session_leader.parent.vpid", + "description": "Virtual process id." + }, + { + "field": "process.session_leader.pid", + "description": "Process id." + }, + { + "field": "process.session_leader.vpid", + "description": "Virtual process id." + }, + { + "field": "process.vpid", + "description": "Virtual process id." + } +] diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/response_actions_form.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/response_actions_form.tsx index cefff7db70467..31b5f16c1a037 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/response_actions_form.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/response_actions_form.tsx @@ -53,14 +53,24 @@ export const ResponseActionsForm = ({ const fieldErrors = reduce>( map(items, 'path'), (acc, path) => { - if (fields[`${path}.params`]?.errors?.length) { - acc.push({ - type: upperFirst((fields[`${path}.actionTypeId`].value as string).substring(1)), - errors: map(fields[`${path}.params`].errors, 'message'), - }); - return acc; - } + map(fields, (_, name) => { + const paramsPath = `${path}.params`; + + if (name.includes(paramsPath)) { + if (fields[name]?.errors?.length) { + const responseActionType = upperFirst( + (fields[`${path}.actionTypeId`].value as string).substring(1) + ); + acc.push({ + type: responseActionType, + errors: map(fields[name].errors, 'message'), + }); + } + return acc; + } + return acc; + }); return acc; }, [] diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/use_supported_response_action_types.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/use_supported_response_action_types.tsx index 1d1346f9090c2..aed3d0302f05c 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/use_supported_response_action_types.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/use_supported_response_action_types.tsx @@ -17,8 +17,8 @@ export const useSupportedResponseActionTypes = () => { >(); const isEndpointEnabled = useIsExperimentalFeatureEnabled('endpointResponseActionsEnabled'); - const { canIsolateHost } = useUserPrivileges().endpointPrivileges; - + const { canIsolateHost, canKillProcess, canSuspendProcess } = + useUserPrivileges().endpointPrivileges; const enabledFeatures = useMemo( () => ({ endpoint: isEndpointEnabled, @@ -28,9 +28,9 @@ export const useSupportedResponseActionTypes = () => { const userHasPermissionsToExecute = useMemo( () => ({ - endpoint: canIsolateHost, + endpoint: canIsolateHost || canKillProcess || canSuspendProcess, }), - [canIsolateHost] + [canIsolateHost, canKillProcess, canSuspendProcess] ); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx index b1a838ba74598..1153cced85ba9 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx @@ -529,4 +529,41 @@ describe('GroupedAlertsTable', () => { } }); }); + + it('sends telemetry data when selected group changes', () => { + jest + .spyOn(window.localStorage, 'getItem') + .mockReturnValue(getMockStorageState(['kibana.alert.rule.name'])); + store = createMockStore({ + ...mockGlobalState, + groups: { + [testProps.tableId]: { + options: mockOptions, + activeGroups: ['kibana.alert.rule.name'], + }, + }, + }); + + const { getByTestId } = render( + + + + ); + + fireEvent.click(getByTestId('group-selector-dropdown')); + fireEvent.click(getByTestId('panel-user.name')); + + expect(mockedTelemetry.reportAlertsGroupingChanged).toHaveBeenCalledWith({ + groupByField: 'user.name', + tableId: testProps.tableId, + }); + + fireEvent.click(getByTestId('group-selector-dropdown')); + fireEvent.click(getByTestId('panel-host.name')); + + expect(mockedTelemetry.reportAlertsGroupingChanged).toHaveBeenCalledWith({ + groupByField: 'host.name', + tableId: testProps.tableId, + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx index f48a288dad64c..3382a94d6c70c 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx @@ -76,8 +76,8 @@ const GroupedAlertsTableComponent: React.FC = (props) const { onGroupChange, onGroupToggle } = useMemo( () => ({ - onGroupChange: (param: { groupByField: string; tableId: string }) => { - telemetry.reportAlertsGroupingChanged(param); + onGroupChange: ({ groupByField, tableId }: { groupByField: string; tableId: string }) => { + telemetry.reportAlertsGroupingChanged({ groupByField, tableId }); }, onGroupToggle: (param: { isOpen: boolean; diff --git a/x-pack/plugins/security_solution/public/detections/components/endpoint_responder/use_responder_action_data.ts b/x-pack/plugins/security_solution/public/detections/components/endpoint_responder/use_responder_action_data.ts index 8a4d98ae76f90..62772fa029e66 100644 --- a/x-pack/plugins/security_solution/public/detections/components/endpoint_responder/use_responder_action_data.ts +++ b/x-pack/plugins/security_solution/public/detections/components/endpoint_responder/use_responder_action_data.ts @@ -39,7 +39,7 @@ const getThirdPartyAgentInfo = ( ) as ResponseActionAgentType, }, host: { - name: getFieldValue({ category: 'host', field: 'host.os.name' }, eventData), + name: getFieldValue({ category: 'host', field: 'host.name' }, eventData), os: { name: getFieldValue({ category: 'host', field: 'host.os.name' }, eventData), family: getFieldValue({ category: 'host', field: 'host.os.family' }, eventData), diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/mitre_attack.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/mitre_attack.test.tsx index 4768be2ad278a..41a7e7b0e3da9 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/mitre_attack.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/mitre_attack.test.tsx @@ -19,7 +19,8 @@ const renderMitreAttack = (contextValue: RightPanelContext) => ); -describe('', () => { +// FLAKY: https://github.com/elastic/kibana/issues/176002 +describe.skip('', () => { it('should render mitre attack information', async () => { const contextValue = { searchHit: mockSearchHit } as unknown as RightPanelContext; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/action_log_expanded_tray.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/action_log_expanded_tray.tsx index 03268178bc3d9..5a44f6584520a 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/action_log_expanded_tray.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/action_log_expanded_tray.tsx @@ -9,6 +9,8 @@ import React, { memo, useMemo } from 'react'; import { EuiCodeBlock, EuiDescriptionList, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { css, euiStyled } from '@kbn/kibana-react-plugin/common'; import { map } from 'lodash'; +import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; +import { getAgentTypeName } from '../../../../common/translations'; import { RESPONSE_ACTION_API_COMMAND_TO_CONSOLE_COMMAND_MAP } from '../../../../../common/endpoint/service/response_actions/constants'; import { isExecuteAction, @@ -178,7 +180,19 @@ export const ActionsLogExpandedTray = memo<{ }>(({ action, 'data-test-subj': dataTestSubj }) => { const getTestId = useTestIdGenerator(dataTestSubj); - const { hosts, startedAt, completedAt, command: _command, comment, parameters } = action; + const isSentinelOneV1Enabled = useIsExperimentalFeatureEnabled( + 'responseActionsSentinelOneV1Enabled' + ); + + const { + hosts, + startedAt, + completedAt, + command: _command, + comment, + parameters, + agentType, + } = action; const parametersList = useMemo( () => @@ -192,45 +206,61 @@ export const ActionsLogExpandedTray = memo<{ const command = RESPONSE_ACTION_API_COMMAND_TO_CONSOLE_COMMAND_MAP[_command]; - const dataList = useMemo( - () => - [ - { - title: OUTPUT_MESSAGES.expandSection.placedAt, - description: `${startedAt}`, - }, - { - title: OUTPUT_MESSAGES.expandSection.startedAt, - description: `${startedAt}`, - }, - { - title: OUTPUT_MESSAGES.expandSection.completedAt, - description: `${completedAt ?? emptyValue}`, - }, - { - title: OUTPUT_MESSAGES.expandSection.input, - description: `${command}`, - }, - { - title: OUTPUT_MESSAGES.expandSection.parameters, - description: parametersList ? parametersList.join(', ') : emptyValue, - }, - { - title: OUTPUT_MESSAGES.expandSection.comment, - description: comment ? comment : emptyValue, - }, - { - title: OUTPUT_MESSAGES.expandSection.hostname, - description: map(hosts, (host) => host.name).join(', ') || emptyValue, - }, - ].map(({ title, description }) => { - return { - title: {title}, - description: {description}, - }; - }), - [command, comment, completedAt, hosts, parametersList, startedAt] - ); + const dataList = useMemo(() => { + const list = [ + { + title: OUTPUT_MESSAGES.expandSection.placedAt, + description: `${startedAt}`, + }, + { + title: OUTPUT_MESSAGES.expandSection.startedAt, + description: `${startedAt}`, + }, + { + title: OUTPUT_MESSAGES.expandSection.completedAt, + description: `${completedAt ?? emptyValue}`, + }, + { + title: OUTPUT_MESSAGES.expandSection.input, + description: `${command}`, + }, + { + title: OUTPUT_MESSAGES.expandSection.parameters, + description: parametersList ? parametersList.join(', ') : emptyValue, + }, + { + title: OUTPUT_MESSAGES.expandSection.comment, + description: comment ? comment : emptyValue, + }, + { + title: OUTPUT_MESSAGES.expandSection.hostname, + description: map(hosts, (host) => host.name).join(', ') || emptyValue, + }, + ]; + + if (isSentinelOneV1Enabled) { + list.push({ + title: OUTPUT_MESSAGES.expandSection.agentType, + description: getAgentTypeName(agentType) || emptyValue, + }); + } + + return list.map(({ title, description }) => { + return { + title: {title}, + description: {description}, + }; + }); + }, [ + agentType, + command, + comment, + completedAt, + hosts, + isSentinelOneV1Enabled, + parametersList, + startedAt, + ]); const outputList = useMemo( () => [ diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx index 36d5d8d1f556b..b8e08f557aa2d 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx @@ -189,18 +189,28 @@ const getTypesFilterInitialState = ( // v8.13 onwards // for showing agent types and action types in the same filter if (isSentinelOneV1Enabled) { + if (!isFlyout) { + return [ + { + label: FILTER_NAMES.agentTypes, + isGroupLabel: true, + }, + ...RESPONSE_ACTION_AGENT_TYPE.map((type) => + getFilterOptions({ + key: type, + label: getAgentTypeName(type), + checked: !isFlyout && agentTypes?.includes(type) ? 'on' : undefined, + }) + ), + { + label: FILTER_NAMES.actionTypes, + isGroupLabel: true, + }, + ...defaultFilterOptions, + ]; + } + return [ - { - label: FILTER_NAMES.agentTypes, - isGroupLabel: true, - }, - ...RESPONSE_ACTION_AGENT_TYPE.map((type) => - getFilterOptions({ - key: type, - label: getAgentTypeName(type), - checked: !isFlyout && agentTypes?.includes(type) ? 'on' : undefined, - }) - ), { label: FILTER_NAMES.actionTypes, isGroupLabel: true, @@ -336,7 +346,10 @@ export const useActionsLogFilter = ({ () => items.filter((item) => item.checked === 'on').length, [items] ); - const numFilters = useMemo(() => items.filter((item) => item.checked !== 'on').length, [items]); + const numFilters = useMemo( + () => items.filter((item) => item.key && item.checked !== 'on').length, + [items] + ); return { areHostsSelectedOnMount, diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx index 2b909e637bd20..3486da1191b4c 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx @@ -28,8 +28,10 @@ import { getActionListMock } from '../mocks'; import { useGetEndpointsList } from '../../../hooks/endpoint/use_get_endpoints_list'; import { v4 as uuidv4 } from 'uuid'; import { + RESPONSE_ACTION_AGENT_TYPE, RESPONSE_ACTION_API_COMMAND_TO_CONSOLE_COMMAND_MAP, RESPONSE_ACTION_API_COMMANDS_NAMES, + RESPONSE_ACTION_TYPE, } from '../../../../../common/endpoint/service/response_actions/constants'; import { useUserPrivileges as _useUserPrivileges } from '../../../../common/components/user_privileges'; import { responseActionsHttpMocks } from '../../../mocks/response_actions_http_mocks'; @@ -571,6 +573,30 @@ describe('Response actions history', () => { ); }); + it('should contain agent type info in each expanded row', async () => { + mockedContext.setExperimentalFlag({ responseActionsSentinelOneV1Enabled: true }); + render(); + const { getAllByTestId } = renderResult; + + const expandButtons = getAllByTestId(`${testPrefix}-expand-button`); + expandButtons.map((button) => userEvent.click(button)); + const trays = getAllByTestId(`${testPrefix}-details-tray`); + expect(trays).toBeTruthy(); + expect(Array.from(trays[0].querySelectorAll('dt')).map((title) => title.textContent)).toEqual( + [ + 'Command placed', + 'Execution started on', + 'Execution completed', + 'Input', + 'Parameters', + 'Comment', + 'Hostname', + 'Agent type', + 'Output:', + ] + ); + }); + it('should refresh data when autoRefresh is toggled on', async () => { const listHookResponse = getBaseMockedActionList(); useGetEndpointActionListMock.mockReturnValue(listHookResponse); @@ -1380,4 +1406,49 @@ describe('Response actions history', () => { ); }); }); + + describe('Types filter', () => { + const filterPrefix = 'types-filter'; + it('should show a list of action types when opened', () => { + render(); + const { getByTestId, getAllByTestId } = renderResult; + + userEvent.click(getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`)); + const filterList = getByTestId(`${testPrefix}-${filterPrefix}-popoverList`); + expect(filterList).toBeTruthy(); + expect(getAllByTestId(`${filterPrefix}-option`).length).toEqual(RESPONSE_ACTION_TYPE.length); + expect(getAllByTestId(`${filterPrefix}-option`).map((option) => option.textContent)).toEqual([ + 'Triggered by rule', + 'Triggered manually', + ]); + }); + + it('should show a list of agent and action types when opened in page view', () => { + mockedContext.setExperimentalFlag({ responseActionsSentinelOneV1Enabled: true }); + render({ isFlyout: false }); + const { getByTestId, getAllByTestId } = renderResult; + + userEvent.click(getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`)); + const filterList = getByTestId(`${testPrefix}-${filterPrefix}-popoverList`); + expect(filterList).toBeTruthy(); + expect(getAllByTestId(`${filterPrefix}-option`).length).toEqual( + [...RESPONSE_ACTION_AGENT_TYPE, ...RESPONSE_ACTION_TYPE].length + ); + expect(getAllByTestId(`${filterPrefix}-option`).map((option) => option.textContent)).toEqual([ + 'Elastic Defend', + 'SentinelOne', + 'Triggered by rule', + 'Triggered manually', + ]); + }); + + it('should have `clear all` button `disabled` when no selected values', () => { + render(); + const { getByTestId } = renderResult; + + userEvent.click(getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`)); + const clearAllButton = getByTestId(`${testPrefix}-${filterPrefix}-clearAllButton`); + expect(clearAllButton.hasAttribute('disabled')).toBeTruthy(); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx index 907348790e92d..b11f31bbd1b72 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx @@ -77,6 +77,12 @@ export const OUTPUT_MESSAGES = Object.freeze({ defaultMessage: 'Hostname', } ), + agentType: i18n.translate( + 'xpack.securitySolution.responseActionsList.list.item.expandSection.agentType', + { + defaultMessage: 'Agent type', + } + ), }, }); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/automated_response_actions.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/automated_response_actions.cy.ts index 1948434b39c9f..38261547f8d27 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/automated_response_actions.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/automated_response_actions.cy.ts @@ -11,7 +11,7 @@ import { closeAllToasts } from '../../tasks/toasts'; import { toggleRuleOffAndOn, visitRuleAlerts } from '../../tasks/isolate'; import { cleanupRule, loadRule } from '../../tasks/api_fixtures'; import { login } from '../../tasks/login'; -import { disableExpandableFlyoutAdvancedSettings, loadPage } from '../../tasks/common'; +import { loadPage } from '../../tasks/common'; import type { IndexedFleetEndpointPolicyResponse } from '../../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy'; import { createAgentPolicyTask, getEndpointIntegrationVersion } from '../../tasks/fleet'; import { changeAlertsFilter } from '../../tasks/alerts'; @@ -23,14 +23,16 @@ import { enableAllPolicyProtections } from '../../tasks/endpoint_policy'; describe( 'Automated Response Actions', { - tags: [ - '@ess', - '@serverless', - // Not supported in serverless! - // The `disableExpandableFlyoutAdvancedSettings()` fails because the API - // `internal/kibana/settings` is not accessible in serverless - '@brokenInServerless', - ], + tags: ['@ess', '@serverless'], + env: { + ftrConfig: { + kbnServerArgs: [ + `--xpack.securitySolution.enableExperimental=${JSON.stringify([ + 'automatedProcessActionsEnabled', + ])}`, + ], + }, + }, }, () => { let indexedPolicy: IndexedFleetEndpointPolicyResponse; @@ -67,16 +69,11 @@ describe( } }); - const hostname = new URL(Cypress.env('FLEET_SERVER_URL')).port; - const fleetHostname = `dev-fleet-server.${hostname}`; - beforeEach(() => { login(); - disableExpandableFlyoutAdvancedSettings(); }); - // FLAKY: https://github.com/elastic/kibana/issues/169828 - describe.skip('From alerts', () => { + describe('From alerts', () => { let ruleId: string; let ruleName: string; @@ -102,18 +99,14 @@ describe( visitRuleAlerts(ruleName); closeAllToasts(); - changeAlertsFilter('event.category: "file"'); - cy.getByTestSubj('expand-event').first().click(); - cy.getByTestSubj('responseActionsViewTab').click(); - cy.getByTestSubj('response-actions-notification').should('not.have.text', '0'); - - cy.getByTestSubj(`response-results-${createdHost.hostname}-details-tray`) - .should('contain', 'isolate completed successfully') - .and('contain', createdHost.hostname); + changeAlertsFilter('process.name: "sshd"'); + cy.getByTestSubj('expand-event').eq(0).click(); + cy.getByTestSubj('securitySolutionFlyoutNavigationExpandDetailButton').click(); + cy.getByTestSubj('securitySolutionFlyoutResponseTab').click(); - cy.getByTestSubj(`response-results-${fleetHostname}-details-tray`) - .should('contain', 'The host does not have Elastic Defend integration installed') - .and('contain', 'dev-fleet-server'); + cy.contains(/isolate is pending|isolate completed successfully/g); + cy.contains(/kill-process is pending|kill-process completed successfully/g); + cy.contains('The action was called with a non-existing event field name: entity_id'); }); }); } diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/form.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/form.cy.ts index 27458ef09e15b..4fa0bb3b0c2e0 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/form.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/form.cy.ts @@ -17,22 +17,26 @@ import { cleanupRule, generateRandomStringName, loadRule } from '../../tasks/api import { ResponseActionTypesEnum } from '../../../../../common/api/detection_engine'; import { login, ROLE } from '../../tasks/login'; +export const RESPONSE_ACTIONS_ERRORS = 'response-actions-error'; + describe( 'Form', { - tags: [ - '@ess', - '@serverless', - - // Not supported in serverless! Test suite uses custom roles - '@brokenInServerless', - ], + tags: ['@ess', '@serverless'], + env: { + ftrConfig: { + kbnServerArgs: [ + `--xpack.securitySolution.enableExperimental=${JSON.stringify([ + 'automatedProcessActionsEnabled', + ])}`, + ], + }, + }, }, () => { - // FLAKY: https://github.com/elastic/kibana/issues/169334 - describe.skip('User with no access can not create an endpoint response action', () => { + describe('User with no access can not create an endpoint response action', () => { beforeEach(() => { - login(ROLE.endpoint_response_actions_no_access); + login(ROLE.rule_author); }); it('no endpoint response action option during rule creation', () => { @@ -43,11 +47,12 @@ describe( describe('User with access can create and save an endpoint response action', () => { const testedCommand = 'isolate'; + const secondTestedCommand = 'suspend-process'; let ruleId: string; const [ruleName, ruleDescription] = generateRandomStringName(2); beforeEach(() => { - login(ROLE.endpoint_response_actions_access); + login(ROLE.soc_manager); }); afterEach(() => { if (ruleId) { @@ -75,20 +80,53 @@ describe( }); cy.getByTestSubj(`command-type-${testedCommand}`).should('not.have.attr', 'disabled'); cy.getByTestSubj(`command-type-${testedCommand}`).click(); + + addEndpointResponseAction(); + focusAndOpenCommandDropdown(1); + cy.getByTestSubj(`command-type-${secondTestedCommand}`).click(); + cy.getByTestSubj('config-overwrite-toggle').click(); + cy.getByTestSubj('config-custom-field-name').should('have.value', ''); + cy.intercept('POST', '/api/detection_engine/rules', (request) => { - const result = { + const isolateResult = { action_type_id: ResponseActionTypesEnum['.endpoint'], params: { command: testedCommand, comment: 'example1', }, }; - expect(request.body.response_actions[0]).to.deep.equal(result); + const processResult = { + action_type_id: ResponseActionTypesEnum['.endpoint'], + params: { + command: secondTestedCommand, + comment: 'example1', + config: { + field: 'process.entity_id', + overwrite: false, + }, + }, + }; + expect(request.body.response_actions[0]).to.deep.equal(isolateResult); + expect(request.body.response_actions[1]).to.deep.equal(processResult); request.continue((response) => { ruleId = response.body.id; response.send(response.body); }); }); + cy.getByTestSubj(RESPONSE_ACTIONS_ERRORS).should('not.exist'); + + cy.getByTestSubj('create-enabled-false').click(); + + cy.getByTestSubj(RESPONSE_ACTIONS_ERRORS).within(() => { + cy.contains( + 'Custom field name is a required field when process.pid toggle is turned off' + ); + }); + + cy.getByTestSubj(`response-actions-list-item-1`).within(() => { + cy.getByTestSubj('config-custom-field-name').type('process.entity_id{downArrow}{enter}'); + }); + cy.getByTestSubj('create-enabled-false').click(); cy.contains(`${ruleName} was created`); }); @@ -97,11 +135,11 @@ describe( describe('User with access can edit and delete an endpoint response action', () => { let ruleId: string; let ruleName: string; - const testedCommand = 'isolate'; - const newDescription = 'Example isolate host description'; + const newDescription = 'Example suspend process description'; + const secondTestedCommand = 'suspend-process'; beforeEach(() => { - login(ROLE.endpoint_response_actions_access); + login(ROLE.soc_manager); loadRule().then((res) => { ruleId = res.id; ruleName = res.name; @@ -115,24 +153,30 @@ describe( visitRuleActions(ruleId); cy.getByTestSubj('edit-rule-actions-tab').click(); - cy.getByTestSubj(`response-actions-list-item-0`).within(() => { - cy.getByTestSubj('input').should('have.value', 'Isolate host'); - cy.getByTestSubj('input').should('have.value', 'Isolate host'); + cy.getByTestSubj(`response-actions-list-item-1`).within(() => { + cy.getByTestSubj('input').should('have.value', 'Suspend host'); cy.getByTestSubj('input').type(`{selectall}{backspace}${newDescription}`); - cy.getByTestSubj('commandTypeField').click(); + cy.getByTestSubj('config-overwrite-toggle').click(); + cy.getByTestSubj('config-custom-field-name').should('have.value', ''); + cy.getByTestSubj('config-overwrite-toggle').click(); + cy.getByTestSubj('config-custom-field-name').type('process.entity_id{downArrow}{enter}'); }); - validateAvailableCommands(); + cy.intercept('PUT', '/api/detection_engine/rules').as('updateResponseAction'); cy.getByTestSubj('ruleEditSubmitButton').click(); cy.wait('@updateResponseAction').should(({ request }) => { const query = { - action_type_id: ResponseActionTypesEnum['.endpoint'], params: { - command: testedCommand, + command: secondTestedCommand, comment: newDescription, + config: { + field: 'process.entity_id', + overwrite: false, + }, }, + action_type_id: ResponseActionTypesEnum['.endpoint'], }; - expect(request.body.response_actions[0]).to.deep.equal(query); + expect(request.body.response_actions[1]).to.deep.equal(query); }); cy.contains(`${ruleName} was saved`).should('exist'); }); @@ -147,7 +191,7 @@ describe( cy.intercept('PUT', '/api/detection_engine/rules').as('deleteResponseAction'); cy.getByTestSubj('ruleEditSubmitButton').click(); cy.wait('@deleteResponseAction').should(({ request }) => { - expect(request.body.response_actions).to.be.equal(undefined); + expect(request.body.response_actions.length).to.be.equal(2); }); cy.contains(`${ruleName} was saved`).should('exist'); }); @@ -157,9 +201,10 @@ describe( const [ruleName, ruleDescription] = generateRandomStringName(2); beforeEach(() => { - login(ROLE.endpoint_response_actions_no_access); + login(ROLE.rule_author); }); + // let currentUrl it('response actions are disabled', () => { fillUpNewRule(ruleName, ruleDescription); cy.getByTestSubj('response-actions-wrapper').within(() => { @@ -174,7 +219,7 @@ describe( let ruleId: string; beforeEach(() => { - login(ROLE.endpoint_response_actions_no_access); + login(ROLE.rule_author); loadRule().then((res) => { ruleId = res.id; }); @@ -200,8 +245,8 @@ describe( // Try removing action cy.getByTestSubj('remove-response-action').click({ force: true }); }); - cy.getByTestSubj(`response-actions-list-item-0`).should('exist'); - tryAddingDisabledResponseAction(1); + cy.getByTestSubj(`response-actions-list-item-2`).should('exist'); + tryAddingDisabledResponseAction(3); }); }); } diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/no_license.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/no_license.cy.ts index d1449672bed26..d120459b64ea3 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/no_license.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/no_license.cy.ts @@ -6,7 +6,6 @@ */ import { navigateToAlertsList } from '../../screens/alerts'; -import { disableExpandableFlyoutAdvancedSettings } from '../../tasks/common'; import { closeAllToasts } from '../../tasks/toasts'; import { fillUpNewRule } from '../../tasks/response_actions'; import { login, ROLE } from '../../tasks/login'; @@ -39,7 +38,6 @@ describe('No License', { tags: '@ess', env: { ftrConfig: { license: 'basic' } } const [endpointAgentId, endpointHostname] = generateRandomStringName(2); beforeEach(() => { login(); - disableExpandableFlyoutAdvancedSettings(); indexEndpointRuleAlerts({ endpointAgentId, endpointHostname, @@ -73,8 +71,8 @@ describe('No License', { tags: '@ess', env: { ftrConfig: { license: 'basic' } } navigateToAlertsList(`query=(language:kuery,query:'agent.id: "${endpointAgentId}" ')`); closeAllToasts(); cy.getByTestSubj('expand-event').first().click(); - cy.getByTestSubj('response-actions-notification').should('not.have.text', '0'); - cy.getByTestSubj('responseActionsViewTab').click(); + cy.getByTestSubj('securitySolutionFlyoutNavigationExpandDetailButton').click(); + cy.getByTestSubj('securitySolutionFlyoutResponseTab').click(); cy.contains('Permission denied'); cy.contains( 'To access these results, ask your administrator for Elastic Defend Kibana privileges.' diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/results.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/results.cy.ts index 10272e5600583..344c4b0b3bd21 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/results.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/results.cy.ts @@ -5,9 +5,8 @@ * 2.0. */ -import { disableExpandableFlyoutAdvancedSettings } from '../../tasks/common'; +import { navigateToAlertsList } from '../../screens/alerts'; import { generateRandomStringName } from '../../tasks/utils'; -import { APP_ALERTS_PATH } from '../../../../../common/constants'; import { closeAllToasts } from '../../tasks/toasts'; import { indexEndpointHosts } from '../../tasks/index_endpoint_hosts'; import type { ReturnTypeFromChainable } from '../../types'; @@ -50,57 +49,36 @@ describe('Results', { tags: ['@ess', '@serverless'] }, () => { } }); - describe( - 'see results when has RBAC', - { - // Not supported in serverless! - // The `disableExpandableFlyoutAdvancedSettings()` fails because the API - // `internal/kibana/settings` is not accessible in serverless - tags: ['@brokenInServerless'], - }, - () => { - before(() => { - login(ROLE.endpoint_response_actions_access); - disableExpandableFlyoutAdvancedSettings(); - }); + describe('see results when has RBAC', () => { + before(() => { + login(ROLE.soc_manager); + }); - it('see endpoint action', () => { - cy.visit(APP_ALERTS_PATH); - closeAllToasts(); - cy.getByTestSubj('expand-event').first().click(); - cy.getByTestSubj('response-actions-notification').should('not.have.text', '0'); - cy.getByTestSubj('responseActionsViewTab').click(); - cy.getByTestSubj('endpoint-results-comment'); - cy.contains(/isolate is pending|isolate completed successfully/g); - }); - } - ); - describe( - 'do not see results results when does not have RBAC', - { - // Not supported in serverless! - // The `disableExpandableFlyoutAdvancedSettings()` fails because the API - // `internal/kibana/settings` is not accessible in serverless - tags: ['@brokenInServerless'], - }, - () => { - before(() => { - login(ROLE.endpoint_response_actions_no_access); - disableExpandableFlyoutAdvancedSettings(); - }); + it('see endpoint action', () => { + navigateToAlertsList(`query=(language:kuery,query:'_id: ${alertData?.alerts[0]._id}')`); + closeAllToasts(); + cy.getByTestSubj('expand-event').first().click(); + cy.getByTestSubj('securitySolutionFlyoutNavigationExpandDetailButton').click(); + cy.getByTestSubj('securitySolutionFlyoutResponseTab').click(); + cy.contains(/isolate is pending|isolate completed successfully/g); + }); + }); + describe('do not see results results when does not have RBAC', () => { + before(() => { + login(ROLE.t1_analyst); + }); - it('show the permission denied callout', () => { - cy.visit(APP_ALERTS_PATH); - closeAllToasts(); + it('show the permission denied callout', () => { + navigateToAlertsList(`query=(language:kuery,query:'_id: ${alertData?.alerts[0]._id}')`); + closeAllToasts(); - cy.getByTestSubj('expand-event').first().click(); - cy.getByTestSubj('response-actions-notification').should('not.have.text', '0'); - cy.getByTestSubj('responseActionsViewTab').click(); - cy.contains('Permission denied'); - cy.contains( - 'To access these results, ask your administrator for Elastic Defend Kibana privileges.' - ); - }); - } - ); + cy.getByTestSubj('expand-event').first().click(); + cy.getByTestSubj('securitySolutionFlyoutNavigationExpandDetailButton').click(); + cy.getByTestSubj('securitySolutionFlyoutResponseTab').click(); + cy.contains('Permission denied'); + cy.contains( + 'To access these results, ask your administrator for Elastic Defend Kibana privileges.' + ); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/api_fixtures.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/api_fixtures.ts index c948062c994c0..1bd0e5652b442 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/tasks/api_fixtures.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/api_fixtures.ts @@ -69,6 +69,28 @@ export const loadRule = (body = {}, includeResponseActions = true) => params: { command: 'isolate', comment: 'Isolate host' }, action_type_id: '.endpoint', }, + { + params: { + command: 'suspend-process', + comment: 'Suspend host', + config: { + field: 'entity_id', + overwrite: false, + }, + }, + action_type_id: '.endpoint', + }, + { + params: { + command: 'kill-process', + comment: 'Kill host', + config: { + field: '', + overwrite: true, + }, + }, + action_type_id: '.endpoint', + }, ], } : {}), diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/response_actions.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/response_actions.ts index 126d637f07edb..a03d5a2ed483d 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/tasks/response_actions.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/response_actions.ts @@ -25,11 +25,19 @@ import type { ResponseActionsApiCommandNames } from '../../../../common/endpoint import { ENABLED_AUTOMATED_RESPONSE_ACTION_COMMANDS } from '../../../../common/endpoint/service/response_actions/constants'; export const validateAvailableCommands = () => { - cy.get('[data-test-subj^="command-type"]').should( - 'have.length', - ENABLED_AUTOMATED_RESPONSE_ACTION_COMMANDS.length - ); - ENABLED_AUTOMATED_RESPONSE_ACTION_COMMANDS.forEach((command) => { + // TODO: TC- use ENABLED_AUTOMATED_RESPONSE_ACTION_COMMANDS when we go GA with automated process actions + const config = Cypress.config(); + const automatedActionsPAttern = /automatedProcessActionsEnabled/; + const automatedProcessActionsEnabled = + config.env.ftrConfig.kbnServerArgs[0].match(automatedActionsPAttern); + + const enabledActions = [ + ...ENABLED_AUTOMATED_RESPONSE_ACTION_COMMANDS, + ...(automatedProcessActionsEnabled ? ['kill-process', 'suspend-process'] : []), + ]; + + cy.get('[data-test-subj^="command-type"]').should('have.length', enabledActions.length); + enabledActions.forEach((command) => { cy.getByTestSubj(`command-type-${command}`); }); }; diff --git a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_get_automated_action_list.ts b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_get_automated_action_list.ts index 03828cba319c2..27b46eddeb042 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_get_automated_action_list.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_get_automated_action_list.ts @@ -151,7 +151,7 @@ const combineResponse = ( return { id: action.EndpointActions.action_id, - agents: action.agent.id as string[], + agents: Array.isArray(action.agent.id) ? action.agent.id : [action.agent.id], agentType: 'endpoint', parameters, ...(alertId?.length ? { alertIds: alertId } : {}), @@ -169,7 +169,7 @@ const combineResponse = ( completedAt: responseData?.completedAt, isCompleted: !!responseData?.isCompleted, isExpired: !!responseData?.isExpired, - wasSuccessful: !!responseData?.isCompleted, + wasSuccessful: responseData.status === 'successful', status: responseData.status, agentState: {}, errors: action.error ? [action.error.message as string] : undefined, diff --git a/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.test.tsx index 19ce46111d1c7..65cd4ffc09908 100644 --- a/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.test.tsx @@ -437,7 +437,7 @@ describe('Response actions history page', () => { }, []); expect(selectedFilterOptions.length).toEqual(1); - expect(selectedFilterOptions).toEqual(['Endpoint. Checked option.']); + expect(selectedFilterOptions).toEqual(['Elastic Defend. Checked option.']); expect(history.location.search).toEqual('?agentTypes=endpoint'); }); }); diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index b5e69d9eeada3..edda193f502fc 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -59,6 +59,7 @@ import type { SecurityAppStore } from './common/store/types'; import { PluginContract } from './plugin_contract'; import { TopValuesPopoverService } from './app/components/top_values_popover/top_values_popover_service'; import { parseConfigSettings, type ConfigSettings } from '../common/config_settings'; +import { getExternalReferenceAttachmentEndpointRegular } from './cases/attachments/external_reference'; export class Plugin implements IPlugin { /** @@ -108,6 +109,7 @@ export class Plugin implements IPlugin(); private storage = new Storage(localStorage); @@ -272,6 +274,10 @@ export class Plugin implements IPlugin = [ const DEFAULT_REGION = 'aws-eu-west-1'; const PROJECT_NAME_PREFIX = 'kibana-cypress-security-solution-ephemeral'; -const BASE_ENV_URL = 'https://console.qa.cld.elstc.co'; +const BASE_ENV_URL = `${process.env.QA_CONSOLE_URL}`; let log: ToolingLog; const API_HEADERS = Object.freeze({ 'kbn-xsrf': 'cypress-creds', @@ -96,6 +108,22 @@ async function createSecurityProject( product_types: productTypes, }; + log.info(`Kibana override flag equals to ${process.env.KIBANA_MKI_USE_LATEST_COMMIT}!`); + if ( + process.env.KIBANA_MKI_USE_LATEST_COMMIT && + process.env.KIBANA_MKI_USE_LATEST_COMMIT === '1' + ) { + const kibanaOverrideImage = `${process.env.BUILDKITE_COMMIT?.substring(0, 12)}`; + log.info( + `Overriding Kibana image in the MKI with docker.elastic.co/kibana-ci/kibana-serverless:sec-sol-qg-${kibanaOverrideImage}` + ); + body.overrides = { + kibana: { + docker_image: `docker.elastic.co/kibana-ci/kibana-serverless:sec-sol-qg-${kibanaOverrideImage}`, + }, + }; + } + try { const response = await axios.post(`${BASE_ENV_URL}/api/v1/serverless/projects/security`, body, { headers: { @@ -570,8 +598,11 @@ ${JSON.stringify(cypressConfigFile, null, 2)} KIBANA_USERNAME: credentials.username, KIBANA_PASSWORD: credentials.password, + // Both CLOUD_SERVERLESS and IS_SERVERLESS are used by the cypress tests. CLOUD_SERVERLESS: true, IS_SERVERLESS: true, + // TEST_CLOUD is used by SvlUserManagerProvider to define if testing against cloud. + TEST_CLOUD: 1, }; if (process.env.DEBUG && !process.env.CI) { diff --git a/x-pack/plugins/security_solution/server/config.mock.ts b/x-pack/plugins/security_solution/server/config.mock.ts index 855cec11ab16e..9f61523dbbe8b 100644 --- a/x-pack/plugins/security_solution/server/config.mock.ts +++ b/x-pack/plugins/security_solution/server/config.mock.ts @@ -22,6 +22,7 @@ export const createMockConfig = (): ConfigType => { maxTimelineImportPayloadBytes: 10485760, enableExperimental, packagerTaskInterval: '60s', + packagerTaskTimeout: '5m', packagerTaskPackagePolicyUpdateBatchSize: 10, prebuiltRulesPackageVersion: '', alertMergeStrategy: 'missingFields', diff --git a/x-pack/plugins/security_solution/server/config.ts b/x-pack/plugins/security_solution/server/config.ts index adc8fbfb1174c..4cb9ff479fff1 100644 --- a/x-pack/plugins/security_solution/server/config.ts +++ b/x-pack/plugins/security_solution/server/config.ts @@ -92,14 +92,20 @@ export const configSchema = schema.object({ }), /** - * Artifacts Configuration + * Endpoint Artifacts Configuration: the interval between runs of the task that builds the + * artifacts and associated manifest. */ packagerTaskInterval: schema.string({ defaultValue: '60s' }), + /** + * Endpoint Artifacts Configuration: timeout value for how long the task should run. + */ + packagerTaskTimeout: schema.string({ defaultValue: '20m' }), + /** * Artifacts Configuration for package policy update concurrency */ - packagerTaskPackagePolicyUpdateBatchSize: schema.number({ defaultValue: 10, max: 50, min: 1 }), + packagerTaskPackagePolicyUpdateBatchSize: schema.number({ defaultValue: 25, max: 50, min: 1 }), /** * For internal use. Specify which version of the Detection Rules fleet package to install diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.test.ts b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.test.ts index a0ad1f9712be1..a1988cb7a13ae 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.test.ts @@ -19,7 +19,7 @@ import { getMockArtifacts } from './mocks'; import { InvalidInternalManifestError } from '../../services/artifacts/errors'; import { loggingSystemMock } from '@kbn/core/server/mocks'; -describe('task', () => { +describe('Endpoint artifact packager task', () => { const MOCK_TASK_INSTANCE = { id: `${ManifestTaskConstants.TYPE}:1.0.0`, runAt: new Date(), @@ -170,7 +170,7 @@ describe('task', () => { await runTask(manifestManager); - expect(logger.info).toHaveBeenCalledWith('recovering from invalid internal manifest'); + expect(logger.warn).toHaveBeenCalledWith('recovering from invalid internal manifest'); expect(logger.error).toHaveBeenNthCalledWith(1, expect.any(InvalidInternalManifestError)); }); diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.ts b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.ts index dafa13141a0c6..8547eb6dca11c 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.ts @@ -22,6 +22,10 @@ import { wrapErrorIfNeeded } from '../../utils'; import { EndpointError } from '../../../../common/endpoint/errors'; export const ManifestTaskConstants = { + /** + * No longer used. Timeout value now comes from `xpack.securitySolution.packagerTaskTimeout` + * @deprecated + */ TIMEOUT: '1m', TYPE: 'endpoint:user-artifact-packager', VERSION: '1.0.0', @@ -44,22 +48,37 @@ export class ManifestTask { constructor(setupContract: ManifestTaskSetupContract) { this.endpointAppContext = setupContract.endpointAppContext; this.logger = this.endpointAppContext.logFactory.get(this.getTaskId()); + const { packagerTaskInterval, packagerTaskTimeout, packagerTaskPackagePolicyUpdateBatchSize } = + this.endpointAppContext.serverConfig; + + this.logger.info( + `Registering ${ManifestTaskConstants.TYPE} task with timeout of [${packagerTaskTimeout}], interval of [${packagerTaskInterval}] and policy update batch size of [${packagerTaskPackagePolicyUpdateBatchSize}]` + ); setupContract.taskManager.registerTaskDefinitions({ [ManifestTaskConstants.TYPE]: { title: 'Security Solution Endpoint Exceptions Handler', - timeout: ManifestTaskConstants.TIMEOUT, + timeout: packagerTaskTimeout, createTaskRunner: ({ taskInstance }: { taskInstance: ConcreteTaskInstance }) => { return { run: async () => { - const taskInterval = (await this.endpointAppContext.config()).packagerTaskInterval; - const startTime = new Date().getTime(); + const taskInterval = packagerTaskInterval; + const startTime = new Date(); + + this.logger.info(`Started. Checking for changes to endpoint artifacts`); + await this.runTask(taskInstance.id); + const endTime = new Date().getTime(); - this.logger.debug( - `${ManifestTaskConstants.TYPE} task run took ${endTime - startTime}ms` + + this.logger.info( + `Complete. Task run took ${ + endTime - startTime.getTime() + }ms [ stated: ${startTime.toISOString()} ]` ); + const nextRun = new Date(); + if (taskInterval.endsWith('s')) { const seconds = parseInt(taskInterval.slice(0, -1), 10); nextRun.setSeconds(nextRun.getSeconds() + seconds); @@ -70,12 +89,20 @@ export class ManifestTask { this.logger.error(`Invalid task interval: ${taskInterval}`); return; } + return { state: {}, runAt: nextRun, }; }, - cancel: async () => {}, + cancel: async () => { + // TODO:PT add support for AbortController to Task manager + this.logger.warn( + 'Task run was canceled. Packaging of endpoint artifacts may be taking longer due to the ' + + 'amount of policies/artifacts. Consider increasing the `xpack.securitySolution.packagerTaskTimeout` ' + + 'server configuration setting if this continues' + ); + }, }; }, }, @@ -91,7 +118,7 @@ export class ManifestTask { taskType: ManifestTaskConstants.TYPE, scope: ['securitySolution'], schedule: { - interval: (await this.endpointAppContext.config()).packagerTaskInterval, + interval: this.endpointAppContext.serverConfig.packagerTaskInterval, }, state: {}, params: { version: ManifestTaskConstants.VERSION }, @@ -127,23 +154,29 @@ export class ManifestTask { } try { - let oldManifest: Manifest | null; + let oldManifest: Manifest | null = null; try { // Last manifest we computed, which was saved to ES oldManifest = await manifestManager.getLastComputedManifest(); } catch (e) { + this.logger.error(e); + // Lets recover from a failure in getting the internal manifest map by creating an empty default manifest if (e instanceof InvalidInternalManifestError) { - this.logger.error(e); - this.logger.info('recovering from invalid internal manifest'); + this.logger.warn('recovering from invalid internal manifest'); oldManifest = ManifestManager.createDefaultManifest(); + } else { + this.logger.error( + `unable to recover from error while attempting to retrieve last computed manifest` + ); + + return; } } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - if (oldManifest! == null) { - this.logger.debug('Last computed manifest not available yet'); + if (!oldManifest) { + this.logger.info('Last computed manifest not available yet'); return; } @@ -152,10 +185,17 @@ export class ManifestTask { const diff = newManifest.diff(oldManifest); + this.logger.debug( + `New -vs- old manifest diff counts: ${Object.entries(diff).map( + ([diffType, diffItems]) => `${diffType}: ${diffItems.length}` + )}` + ); + const persistErrors = await manifestManager.pushArtifacts( diff.additions as InternalArtifactCompleteSchema[], newManifest ); + if (persistErrors.length) { reportErrors(this.logger, persistErrors); throw new Error('Unable to persist new artifacts.'); @@ -167,8 +207,9 @@ export class ManifestTask { await manifestManager.commit(newManifest); } - // Try dispatching to ingest-manager package policies + // Dispatch updates to Fleet integration policies with new manifest info const dispatchErrors = await manifestManager.tryDispatch(newManifest); + if (dispatchErrors.length) { reportErrors(this.logger, dispatchErrors); throw new Error('Error dispatching manifest.'); @@ -178,9 +219,11 @@ export class ManifestTask { const deleteErrors = await manifestManager.deleteArtifacts( diff.removals.map((artifact) => getArtifactId(artifact)) ); + if (deleteErrors.length) { reportErrors(this.logger, deleteErrors); } + await manifestManager.cleanup(newManifest); } catch (err) { this.logger.error(wrapErrorIfNeeded(err)); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts index 74f222b64d2b9..4789ff8046e6d 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts @@ -1092,6 +1092,12 @@ describe('Response actions', () => { id: '123-456', }, }, + agent: { + id: '123-456', + }, + host: { + hostname: 'test-host', + }, }, ]), }); @@ -1215,6 +1221,12 @@ describe('Response actions', () => { id: '123-456', }, }, + agent: { + id: '123-456', + }, + host: { + hostname: 'test-host', + }, }, ]), }); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint_actions_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint_actions_client.ts index 2acc6dec8f2a2..9e3c158033835 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint_actions_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint_actions_client.ts @@ -5,13 +5,11 @@ * 2.0. */ -import { stringify } from '../../../utils/stringify'; import type { HapiReadableStream } from '../../../../types'; import type { ResponseActionsApiCommandNames, ResponseActionAgentType, } from '../../../../../common/endpoint/service/response_actions/constants'; -import { updateCases } from '../create/update_cases'; import type { CreateActionPayload } from '../create/types'; import type { ExecuteActionRequestBody, @@ -36,6 +34,8 @@ import type { ResponseActionUploadOutputContent, ResponseActionUploadParameters, SuspendProcessActionOutputContent, + ImmutableObject, + HostMetadataInterface, } from '../../../../../common/endpoint/types'; export class EndpointActionsClient extends ResponseActionsClientImpl { @@ -80,16 +80,19 @@ export class EndpointActionsClient extends ResponseActionsClientImpl { .getActionCreateService() .createAction(createPayload, agentIds.valid); - try { - await updateCases({ - casesClient: this.options.casesClient, - endpointData: agentIds.hosts, - createActionPayload: createPayload, - }); - } catch (err) { - // failures during update of cases should not cause the response action to fail. Just log error - this.log.warn(`failed to update cases: ${err.message}\n${stringify(err)}`); - } + await this.updateCases({ + command, + comment: options.comment, + caseIds: options.case_ids, + alertIds: options.alert_ids, + actionId: response.id, + hosts: agentIds.hosts.map((endpoint: ImmutableObject) => { + return { + hostId: endpoint.agent.id, + hostname: endpoint.host.hostname, + }; + }), + }); return response as TResponse; } diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.test.ts index aaceb2d6aef75..f23bafe0f22a0 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.test.ts @@ -120,6 +120,7 @@ describe('ResponseActionsClientImpl base class', () => { caseIds: ['case-999'], alertIds: [KNOWN_ALERT_ID_1, KNOWN_ALERT_ID_2, KNOWN_ALERT_ID_3], comment: 'this is a case comment', + actionId: 'action-123', hosts: [ { hostId: '1-2-3', @@ -201,76 +202,29 @@ describe('ResponseActionsClientImpl base class', () => { expect(casesClient.attachments.bulkCreate).toHaveBeenLastCalledWith({ attachments: [ { - actions: { - targets: [ - { - endpointId: '1-2-3', - hostname: 'foo-one', - }, - { - endpointId: '4-5-6', - hostname: 'foo-two', - }, - ], - type: 'isolate', - }, - comment: 'this is a case comment', + externalReferenceAttachmentTypeId: 'endpoint', + externalReferenceId: 'action-123', owner: 'securitySolution', - type: 'actions', - }, - { - actions: { - targets: [ - { - endpointId: '1-2-3', - hostname: 'foo-one', - }, - { - endpointId: '4-5-6', - hostname: 'foo-two', - }, - ], - type: 'isolate', - }, - comment: 'this is a case comment', - owner: 'securitySolution', - type: 'actions', - }, - { - actions: { - targets: [ - { - endpointId: '1-2-3', - hostname: 'foo-one', - }, - { - endpointId: '4-5-6', - hostname: 'foo-two', - }, - ], - type: 'isolate', + externalReferenceStorage: { + type: 'elasticSearchDoc', }, - comment: 'this is a case comment', - owner: 'securitySolution', - type: 'actions', - }, - { - actions: { + type: 'externalReference', + externalReferenceMetadata: { + command: 'isolate', + comment: 'this is a case comment', targets: [ { endpointId: '1-2-3', hostname: 'foo-one', + agentType: 'endpoint', }, { endpointId: '4-5-6', hostname: 'foo-two', + agentType: 'endpoint', }, ], - type: 'isolate', }, - comment: 'this is a case comment', - owner: 'securitySolution', - type: 'actions', }, ], caseId: 'case-3', @@ -280,7 +234,7 @@ describe('ResponseActionsClientImpl base class', () => { it('should not error if update to a case fails', async () => { (casesClient.attachments.bulkCreate as jest.Mock).mockImplementation(async (options) => { if (options.caseId === 'case-2') { - throw new Error('update filed to case-2'); + throw new Error('update failed to case-2'); } }); await baseClassMock.updateCases(updateCasesOptions); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.ts index 68e8db5fa1e69..356b9f129f79e 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.ts @@ -9,8 +9,9 @@ import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import type { CasesClient } from '@kbn/cases-plugin/server'; import type { Logger } from '@kbn/logging'; import { v4 as uuidv4 } from 'uuid'; -import { AttachmentType } from '@kbn/cases-plugin/common'; -import type { BulkCreateArgs } from '@kbn/cases-plugin/server/client/attachments/types'; +import { AttachmentType, ExternalReferenceStorageType } from '@kbn/cases-plugin/common'; +import type { CaseAttachments } from '@kbn/cases-plugin/public/types'; + import type { EndpointAppContextService } from '../../../../endpoint_app_context_services'; import { APP_ID } from '../../../../../../common'; import type { @@ -57,6 +58,8 @@ import type { } from '../../../../../../common/api/endpoint'; import type { CreateActionPayload } from '../../create/types'; import { stringify } from '../../../../utils/stringify'; +import { CASE_ATTACHMENT_ENDPOINT_TYPE_ID } from '../../../../../../common/constants'; +import { EMPTY_COMMENT } from '../../../../utils/translations'; export interface ResponseActionsClientOptions { endpointService: EndpointAppContextService; @@ -79,6 +82,8 @@ export interface ResponseActionsClientUpdateCasesOptions { alertIds?: string[]; /** Comment to include in the Case attachment */ comment?: string; + /** The id of the action that was taken */ + actionId: string; } export type ResponseActionsClientWriteActionRequestToEndpointIndexOptions = @@ -119,6 +124,7 @@ export abstract class ResponseActionsClientImpl implements ResponseActionsClient caseIds = [], alertIds = [], comment = '', + actionId, }: ResponseActionsClientUpdateCasesOptions): Promise { if (caseIds.length === 0 && alertIds.length === 0) { this.log.debug(`Nothing to do. 'caseIds' and 'alertIds' are empty`); @@ -166,32 +172,43 @@ export abstract class ResponseActionsClientImpl implements ResponseActionsClient this.log.debug(`Updating cases:\n${stringify(allCases)}`); - // Create an attachment for each case that includes info. about the response actions taken against the hosts - const attachments = allCases.map(() => ({ - type: AttachmentType.actions, - comment, - actions: { - targets: hosts.map(({ hostId: endpointId, hostname }) => ({ endpointId, hostname })), - type: command, + const attachments: CaseAttachments = [ + { + type: AttachmentType.externalReference, + externalReferenceId: actionId, + externalReferenceStorage: { + type: ExternalReferenceStorageType.elasticSearchDoc, + }, + externalReferenceAttachmentTypeId: CASE_ATTACHMENT_ENDPOINT_TYPE_ID, + externalReferenceMetadata: { + targets: hosts.map(({ hostId: endpointId, hostname }) => { + return { + endpointId, + hostname, + agentType: this.agentType, + }; + }), + command, + comment: comment || EMPTY_COMMENT, + }, + owner: APP_ID, }, - owner: APP_ID, - })) as BulkCreateArgs['attachments']; + ]; const casesUpdateResponse = await Promise.all( - allCases.map((caseId) => - casesClient.attachments - .bulkCreate({ + allCases.map(async (caseId) => { + try { + return await casesClient.attachments.bulkCreate({ caseId, attachments, - }) - .catch((err) => { - // Log any error, BUT: do not fail execution - this.log.warn( - `Attempt to update case ID [${caseId}] failed: ${err.message}\n${stringify(err)}` - ); - return null; - }) - ) + }); + } catch (err) { + this.log.warn( + `Attempt to update case ID [${caseId}] failed: ${err.message}\n${stringify(err)}` + ); + return null; + } + }) ); this.log.debug(`Update to cases done:\n${stringify(casesUpdateResponse)}`); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.ts index d196232e83916..dfac74fc8a407 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.ts @@ -182,11 +182,10 @@ export class SentinelOneActionsClient extends ResponseActionsClientImpl { await this.validateRequest(options); await this.sendAction(SUB_ACTION.ISOLATE_HOST, { uuid: options.endpoint_ids[0] }); - const reqIndexOptions: ResponseActionsClientWriteActionRequestToEndpointIndexOptions = { + const actionRequestDoc = await this.writeActionRequestToEndpointIndex({ ...options, command: 'isolate', - }; - const actionRequestDoc = await this.writeActionRequestToEndpointIndex(reqIndexOptions); + }); await this.writeActionResponseToEndpointIndex({ actionId: actionRequestDoc.EndpointActions.action_id, agentId: actionRequestDoc.agent.id, @@ -194,6 +193,19 @@ export class SentinelOneActionsClient extends ResponseActionsClientImpl { command: actionRequestDoc.EndpointActions.data.command, }, }); + await this.updateCases({ + command: 'isolate', + caseIds: options.case_ids, + alertIds: options.alert_ids, + hosts: options.endpoint_ids.map((agentId) => { + return { + hostId: agentId, + hostname: actionRequestDoc.EndpointActions.data.hosts?.[agentId].name ?? '', + }; + }), + comment: options.comment, + actionId: actionRequestDoc.EndpointActions.action_id, + }); return this.fetchActionDetails(actionRequestDoc.EndpointActions.action_id); } @@ -216,6 +228,19 @@ export class SentinelOneActionsClient extends ResponseActionsClientImpl { command: actionRequestDoc.EndpointActions.data.command, }, }); + await this.updateCases({ + command: 'unisolate', + caseIds: options.case_ids, + alertIds: options.alert_ids, + hosts: options.endpoint_ids.map((agentId) => { + return { + hostId: agentId, + hostname: actionRequestDoc.EndpointActions.data.hosts?.[agentId].name ?? '', + }; + }), + comment: options.comment, + actionId: actionRequestDoc.EndpointActions.action_id, + }); return this.fetchActionDetails(actionRequestDoc.EndpointActions.action_id); } diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/create/action_errors.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/create/action_errors.ts index bb17cb2c5ac8a..8f68072e95707 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/create/action_errors.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/create/action_errors.ts @@ -7,17 +7,19 @@ import type { LicenseType } from '@kbn/licensing-plugin/common/types'; import type { EcsError } from '@kbn/ecs'; -import { validateAgents, validateEndpointLicense } from './validate'; +import { validateAgents, validateAlertError, validateEndpointLicense } from './validate'; import type { LicenseService } from '../../../../../common/license/license'; export const addErrorsToActionIfAny = ({ agents, licenseService, minimumLicenseRequired = 'basic', + error, }: { agents: string[]; licenseService: LicenseService; minimumLicenseRequired: LicenseType; + error?: string; }): | { error: { @@ -28,7 +30,8 @@ export const addErrorsToActionIfAny = ({ | undefined => { const licenseError = validateEndpointLicense(licenseService, minimumLicenseRequired); const agentsError = validateAgents(agents); - const alertActionError = licenseError || agentsError; + const actionError = validateAlertError(error); + const alertActionError = licenseError || agentsError || actionError; if (alertActionError) { return { diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/create/update_cases.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/create/update_cases.ts deleted file mode 100644 index 7032a0d7b4725..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/create/update_cases.ts +++ /dev/null @@ -1,84 +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 { GetRelatedCasesByAlertResponse } from '@kbn/cases-plugin/common'; -import { AttachmentType } from '@kbn/cases-plugin/common'; -import type { CasesClient } from '@kbn/cases-plugin/server'; -import type { BulkCreateArgs } from '@kbn/cases-plugin/server/client/attachments/types'; -import { i18n } from '@kbn/i18n'; -import { APP_ID } from '../../../../../common'; -import type { - ImmutableObject, - HostMetadataInterface, - HostMetadata, -} from '../../../../../common/endpoint/types'; -import type { CreateActionPayload } from './types'; - -export const updateCases = async ({ - casesClient, - createActionPayload, - endpointData, -}: { - casesClient?: CasesClient; - createActionPayload: CreateActionPayload; - endpointData: Array>; -}): Promise => { - if (!casesClient) { - return; - } - // convert any alert IDs into cases - let caseIDs: string[] = createActionPayload.case_ids?.slice() || []; - if (createActionPayload.alert_ids && createActionPayload.alert_ids.length > 0) { - const newIDs: string[][] = await Promise.all( - createActionPayload.alert_ids.map(async (alertID: string) => { - const cases: GetRelatedCasesByAlertResponse = await casesClient.cases.getCasesByAlertID({ - alertID, - options: { owner: APP_ID }, - }); - return cases.map((caseInfo): string => { - return caseInfo.id; - }); - }) - ); - caseIDs = caseIDs.concat(...newIDs); - } - caseIDs = [...new Set(caseIDs)]; - - // Update all cases with a comment - if (caseIDs.length > 0) { - const targets = endpointData.map((endpoint: HostMetadata) => ({ - hostname: endpoint.host.hostname, - endpointId: endpoint.agent.id, - })); - - const attachments = caseIDs.map(() => ({ - type: AttachmentType.actions, - comment: createActionPayload.comment || EMPTY_COMMENT, - actions: { - targets, - type: createActionPayload.command, - }, - owner: APP_ID, - })) as BulkCreateArgs['attachments']; - - await Promise.all( - caseIDs.map((caseId) => - casesClient.attachments.bulkCreate({ - caseId, - attachments, - }) - ) - ); - } -}; - -export const EMPTY_COMMENT = i18n.translate( - 'xpack.securitySolution.endpoint.updateCases.emptyComment', - { - defaultMessage: 'No comment provided', - } -); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/create/validate.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/create/validate.ts index 5a6fd84989109..cb831021501c3 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/create/validate.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/create/validate.ts @@ -9,7 +9,10 @@ import { i18n } from '@kbn/i18n'; import type { LicenseType } from '@kbn/licensing-plugin/server'; import type { LicenseService } from '../../../../../common/license'; -export const validateEndpointLicense = (license: LicenseService, licenseType: LicenseType) => { +export const validateEndpointLicense = ( + license: LicenseService, + licenseType: LicenseType +): string | undefined => { const hasEnterpriseLicense = license.isAtLeast(licenseType); if (!hasEnterpriseLicense) { @@ -17,12 +20,18 @@ export const validateEndpointLicense = (license: LicenseService, licenseType: Li } }; -export const validateAgents = (agents: string[]) => { +export const validateAgents = (agents: string[]): string | undefined => { if (!agents.length) { return HOST_NOT_ENROLLED; } }; +export const validateAlertError = (field?: string): string | undefined => { + if (field) { + return FIELD_NOT_EXIST(field); + } +}; + export const LICENSE_TOO_LOW = i18n.translate( 'xpack.securitySolution.responseActionsList.error.licenseTooLow', { @@ -36,3 +45,9 @@ export const HOST_NOT_ENROLLED = i18n.translate( defaultMessage: 'The host does not have Elastic Defend integration installed', } ); + +export const FIELD_NOT_EXIST = (field: string): string => + i18n.translate('xpack.securitySolution.responseActionsList.error.nonExistingFieldName', { + defaultMessage: 'The action was called with a non-existing event field name: {field}', + values: { field }, + }); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/create/write_action_to_indices.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/create/write_action_to_indices.ts index a0626b2e4d883..3e86e8fa9625c 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/create/write_action_to_indices.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/create/write_action_to_indices.ts @@ -72,6 +72,7 @@ export const writeActionToIndices = async ({ agents, licenseService, minimumLicenseRequired, + error: payload.error, }), ...addRuleInfoToAction(payload), }; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/artifact_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/artifact_client.ts index 3e00310a5bb64..24c04ee881b8e 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/artifact_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/artifact_client.ts @@ -11,9 +11,11 @@ import type { ListArtifactsProps, } from '@kbn/fleet-plugin/server'; import type { ListResult } from '@kbn/fleet-plugin/common'; +import type { FetchAllArtifactsOptions } from '@kbn/fleet-plugin/server/services'; import type { InternalArtifactCompleteSchema } from '../../schemas/artifacts'; -export interface EndpointArtifactClientInterface { +export interface EndpointArtifactClientInterface + extends Pick { getArtifact(id: string): Promise; createArtifact(artifact: InternalArtifactCompleteSchema): Promise; @@ -67,6 +69,15 @@ export class EndpointArtifactClient implements EndpointArtifactClientInterface { return this.fleetArtifacts.listArtifacts(options); } + fetchAll({ + // Our default, unlike the Fleet service, is to NOT include the body of + // the artifact, since we really don't need it when processing all artifacts + includeArtifactBody = false, + ...options + }: FetchAllArtifactsOptions = {}): AsyncIterable { + return this.fleetArtifacts.fetchAll({ ...options, includeArtifactBody }); + } + async createArtifact( artifact: InternalArtifactCompleteSchema ): Promise { diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts index 9ff37c67e613d..1d935ccb905d0 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts @@ -39,6 +39,10 @@ import { EndpointError } from '../../../../../common/endpoint/errors'; import type { Artifact } from '@kbn/fleet-plugin/server'; import { AppFeatureSecurityKey } from '@kbn/security-solution-features/keys'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types/src/response/exception_list_item_schema'; +import { + createFetchAllArtifactsIterableMock, + generateArtifactMock, +} from '@kbn/fleet-plugin/server/services/artifacts/mocks'; const getArtifactObject = (artifact: InternalArtifactSchema) => JSON.parse(Buffer.from(artifact.body!, 'base64').toString()); @@ -76,12 +80,9 @@ describe('ManifestManager', () => { const ARTIFACT_NAME_BLOCKLISTS_WINDOWS = 'endpoint-blocklist-windows-v1'; const ARTIFACT_NAME_BLOCKLISTS_LINUX = 'endpoint-blocklist-linux-v1'; - const mockPolicyListIdsResponse = (items: string[]) => - jest.fn().mockResolvedValue({ - items, - page: 1, - per_page: 100, - total: items.length, + const getMockPolicyFetchAllItemIds = (items: string[]) => + jest.fn(async function* () { + yield items; }); let ARTIFACTS: InternalArtifactCompleteSchema[] = []; @@ -200,9 +201,7 @@ describe('ManifestManager', () => { ( manifestManagerContext.artifactClient as jest.Mocked - ).listArtifacts.mockImplementation(async () => { - return { items: ARTIFACTS as Artifact[], total: 100, page: 1, perPage: 100 }; - }); + ).fetchAll.mockReturnValue(createFetchAllArtifactsIterableMock([ARTIFACTS as Artifact[]])); const manifest = await manifestManager.getLastComputedManifest(); @@ -259,33 +258,26 @@ describe('ManifestManager', () => { ( manifestManagerContext.artifactClient as jest.Mocked - ).listArtifacts.mockImplementation(async () => { - // report the MACOS Exceptions artifact as not found - return { - items: [ + ).fetchAll.mockReturnValue( + createFetchAllArtifactsIterableMock([ + // report the MACOS Exceptions artifact as not found + [ ARTIFACT_TRUSTED_APPS_MACOS, ARTIFACT_EXCEPTIONS_WINDOWS, ARTIFACT_TRUSTED_APPS_WINDOWS, ARTIFACTS_BY_ID[ARTIFACT_ID_EXCEPTIONS_LINUX], ] as Artifact[], - total: 100, - page: 1, - perPage: 100, - }; - }); + ]) + ); const manifest = await manifestManager.getLastComputedManifest(); expect(manifest?.getAllArtifacts()).toStrictEqual(ARTIFACTS.slice(1, 5)); - expect(manifestManagerContext.logger.error).toHaveBeenCalledWith( - new InvalidInternalManifestError( - `artifact id [${ARTIFACT_ID_EXCEPTIONS_MACOS}] not found!`, - { - entry: ARTIFACTS_BY_ID[ARTIFACT_ID_EXCEPTIONS_MACOS], - action: 'removed from internal ManifestManger tracking map', - } - ) + expect(manifestManagerContext.logger.warn).toHaveBeenCalledWith( + "Missing artifacts detected! Internal artifact manifest (SavedObject version [2.0.0]) references [1] artifact IDs that don't exist.\n" + + "First 10 below (run with logging set to 'debug' to see all):\n" + + 'endpoint-exceptionlist-macos-v1-96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3' ); }); }); @@ -327,7 +319,9 @@ describe('ManifestManager', () => { const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({}); - context.packagePolicyService.listIds = mockPolicyListIdsResponse([TEST_POLICY_ID_1]); + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ + TEST_POLICY_ID_1, + ]); context.savedObjectsClient.create = jest .fn() @@ -389,7 +383,9 @@ describe('ManifestManager', () => { .mockImplementation((_type: string, object: InternalManifestSchema) => ({ attributes: object, })); - context.packagePolicyService.listIds = mockPolicyListIdsResponse([TEST_POLICY_ID_1]); + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ + TEST_POLICY_ID_1, + ]); const manifest = await manifestManager.buildNewManifest(); @@ -460,7 +456,9 @@ describe('ManifestManager', () => { context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ [ENDPOINT_LIST_ID]: { macos: [exceptionListItem] }, }); - context.packagePolicyService.listIds = mockPolicyListIdsResponse([TEST_POLICY_ID_1]); + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ + TEST_POLICY_ID_1, + ]); context.savedObjectsClient.create = jest .fn() .mockImplementation((_type: string, object: InternalManifestSchema) => ({ @@ -576,7 +574,7 @@ describe('ManifestManager', () => { .mockImplementation((_type: string, object: InternalManifestSchema) => ({ attributes: object, })); - context.packagePolicyService.listIds = mockPolicyListIdsResponse([ + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ TEST_POLICY_ID_1, TEST_POLICY_ID_2, ]); @@ -679,7 +677,7 @@ describe('ManifestManager', () => { linux: [trustedAppListItem, trustedAppListItemPolicy2], }, }); - context.packagePolicyService.listIds = mockPolicyListIdsResponse([ + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ TEST_POLICY_ID_1, TEST_POLICY_ID_2, ]); @@ -795,7 +793,9 @@ describe('ManifestManager', () => { .mockImplementation((_type: string, object: InternalManifestSchema) => ({ attributes: object, })); - context.packagePolicyService.listIds = mockPolicyListIdsResponse([TEST_POLICY_ID_1]); + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ + TEST_POLICY_ID_1, + ]); const manifest = await manifestManager.buildNewManifest(); @@ -878,7 +878,9 @@ describe('ManifestManager', () => { .mockImplementation((_type: string, object: InternalManifestSchema) => ({ attributes: object, })); - context.packagePolicyService.listIds = mockPolicyListIdsResponse([TEST_POLICY_ID_1]); + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ + TEST_POLICY_ID_1, + ]); const manifest = await manifestManager.buildNewManifest(); @@ -960,7 +962,9 @@ describe('ManifestManager', () => { .mockImplementation((_type: string, object: InternalManifestSchema) => ({ attributes: object, })); - context.packagePolicyService.listIds = mockPolicyListIdsResponse([TEST_POLICY_ID_1]); + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ + TEST_POLICY_ID_1, + ]); const manifest = await manifestManager.buildNewManifest(); @@ -1026,7 +1030,9 @@ describe('ManifestManager', () => { .mockImplementation((_type: string, object: InternalManifestSchema) => ({ attributes: object, })); - context.packagePolicyService.listIds = mockPolicyListIdsResponse([TEST_POLICY_ID_1]); + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ + TEST_POLICY_ID_1, + ]); const manifest = await manifestManager.buildNewManifest(); @@ -1068,7 +1074,9 @@ describe('ManifestManager', () => { .mockImplementation((_type: string, object: InternalManifestSchema) => ({ attributes: object, })); - context.packagePolicyService.listIds = mockPolicyListIdsResponse([TEST_POLICY_ID_1]); + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ + TEST_POLICY_ID_1, + ]); const manifest = await manifestManager.buildNewManifest(); @@ -1299,12 +1307,9 @@ describe('ManifestManager', () => { }); describe('tryDispatch', () => { - const mockPolicyListResponse = (items: PackagePolicy[]) => - jest.fn().mockResolvedValue({ - items, - page: 1, - per_page: 100, - total: items.length, + const getMockPolicyFetchAllItems = (items: PackagePolicy[]) => + jest.fn(async function* () { + yield items; }); test('Should not dispatch if no policies', async () => { @@ -1313,8 +1318,7 @@ describe('ManifestManager', () => { const manifest = new Manifest({ soVersion: '1.0.0' }); manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS); - - context.packagePolicyService.list = mockPolicyListResponse([]); + context.packagePolicyService.fetchAllItems = getMockPolicyFetchAllItems([]); await expect(manifestManager.tryDispatch(manifest)).resolves.toStrictEqual([]); @@ -1328,7 +1332,7 @@ describe('ManifestManager', () => { const manifest = new Manifest({ soVersion: '1.0.0' }); manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS); - context.packagePolicyService.list = mockPolicyListResponse([ + context.packagePolicyService.fetchAllItems = getMockPolicyFetchAllItems([ createPackagePolicyWithConfigMock({ id: TEST_POLICY_ID_1 }), ]); @@ -1346,7 +1350,7 @@ describe('ManifestManager', () => { const manifest = new Manifest({ soVersion: '1.0.0' }); manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS); - context.packagePolicyService.list = mockPolicyListResponse([ + context.packagePolicyService.fetchAllItems = getMockPolicyFetchAllItems([ createPackagePolicyWithConfigMock({ id: TEST_POLICY_ID_1, config: { @@ -1378,7 +1382,7 @@ describe('ManifestManager', () => { manifest.addEntry(ARTIFACT_EXCEPTIONS_WINDOWS, TEST_POLICY_ID_2); manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_2); - context.packagePolicyService.list = mockPolicyListResponse([ + context.packagePolicyService.fetchAllItems = getMockPolicyFetchAllItems([ createPackagePolicyWithConfigMock({ id: TEST_POLICY_ID_1, config: { @@ -1446,7 +1450,7 @@ describe('ManifestManager', () => { manifest.addEntry(ARTIFACT_EXCEPTIONS_WINDOWS, TEST_POLICY_ID_2); manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_2); - context.packagePolicyService.list = mockPolicyListResponse([ + context.packagePolicyService.fetchAllItems = getMockPolicyFetchAllItems([ createPackagePolicyWithConfigMock({ id: TEST_POLICY_ID_1, config: { @@ -1516,7 +1520,7 @@ describe('ManifestManager', () => { const manifest = new Manifest({ soVersion: '1.0.0', semanticVersion: '1.0.1' }); manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS); - context.packagePolicyService.list = mockPolicyListResponse([ + context.packagePolicyService.fetchAllItems = getMockPolicyFetchAllItems([ createPackagePolicyWithConfigMock({ id: TEST_POLICY_ID_1, config: { @@ -1557,8 +1561,14 @@ describe('ManifestManager', () => { const context = buildManifestManagerContextMock({}); const manifestManager = new ManifestManager(context); + (context.artifactClient.fetchAll as jest.Mock).mockReturnValue( + createFetchAllArtifactsIterableMock([[generateArtifactMock()]]) + ); + context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({}); - context.packagePolicyService.listIds = mockPolicyListIdsResponse([TEST_POLICY_ID_1]); + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ + TEST_POLICY_ID_1, + ]); context.savedObjectsClient.create = jest .fn() @@ -1581,7 +1591,9 @@ describe('ManifestManager', () => { const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({}); - context.packagePolicyService.listIds = mockPolicyListIdsResponse([TEST_POLICY_ID_1]); + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ + TEST_POLICY_ID_1, + ]); context.savedObjectsClient.create = jest .fn() diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts index 27f528aba2716..a1ec74bc57b09 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts @@ -6,15 +6,17 @@ */ import semver from 'semver'; -import { chunk, isEmpty, isEqual, keyBy } from 'lodash'; +import { isEmpty, isEqual, keyBy } from 'lodash'; import type { ElasticsearchClient } from '@kbn/core/server'; import { type Logger, type SavedObjectsClientContract } from '@kbn/core/server'; import { ENDPOINT_LIST_ID, ENDPOINT_ARTIFACT_LISTS } from '@kbn/securitysolution-list-constants'; -import type { ListResult, PackagePolicy } from '@kbn/fleet-plugin/common'; +import type { PackagePolicy } from '@kbn/fleet-plugin/common'; import type { Artifact, PackagePolicyClient } from '@kbn/fleet-plugin/server'; import type { ExceptionListClient } from '@kbn/lists-plugin/server'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { AppFeatureKey } from '@kbn/security-solution-features/keys'; +import { stringify } from '../../../utils/stringify'; +import { QueueProcessor } from '../../../utils/queue_processor'; import type { AppFeaturesService } from '../../../../lib/app_features_service/app_features_service'; import type { ExperimentalFeatures } from '../../../../../common'; import type { ManifestSchemaVersion } from '../../../../../common/endpoint/schema/common'; @@ -70,24 +72,6 @@ const iterateArtifactsBuildResult = ( } }; -const iterateAllListItems = async ( - pageSupplier: (page: number, perPage: number) => Promise>, - itemCallback: (items: T[]) => void -) => { - let paging = true; - let page = 1; - const perPage = 1000; - - while (paging) { - const { items, total } = await pageSupplier(page, perPage); - - itemCallback(items); - - paging = (page - 1) * perPage + items.length < total; - page++; - } -}; - export interface ManifestManagerContext { savedObjectsClient: SavedObjectsClientContract; artifactClient: EndpointArtifactClientInterface; @@ -407,7 +391,7 @@ export class ManifestManager { } /** - * Writes new artifact SOs. + * Writes new artifact to Fleet * * @param artifacts An InternalArtifactCompleteSchema array representing the artifacts. * @param newManifest A Manifest representing the new manifest @@ -418,7 +402,6 @@ export class ManifestManager { newManifest: Manifest ): Promise { const errors: Error[] = []; - const artifactsToCreate: InternalArtifactCompleteSchema[] = []; for (const artifact of artifacts) { @@ -433,28 +416,58 @@ export class ManifestManager { return errors; } + this.logger.debug(`Creating [${artifactsToCreate.length}] artifacts`); + const { artifacts: fleetArtifacts, errors: createErrors } = await this.artifactClient.bulkCreateArtifacts(artifactsToCreate); + this.logger.info(`Count of artifacts created: ${fleetArtifacts?.length ?? 0}`); + if (createErrors) { errors.push(...createErrors); } + const newArtifactsAddedToManifest: string[] = []; + const artifactsNotCreated: string[] = []; + if (fleetArtifacts) { - const fleetArtfactsByIdentifier: { [key: string]: InternalArtifactCompleteSchema } = {}; + const fleetArtifactsByIdentifier: { [key: string]: InternalArtifactCompleteSchema } = {}; + fleetArtifacts.forEach((fleetArtifact) => { - fleetArtfactsByIdentifier[getArtifactId(fleetArtifact)] = fleetArtifact; + fleetArtifactsByIdentifier[getArtifactId(fleetArtifact)] = fleetArtifact; }); + artifactsToCreate.forEach((artifact) => { const artifactId = getArtifactId(artifact); - const fleetArtifact = fleetArtfactsByIdentifier[artifactId]; + const fleetArtifact = fleetArtifactsByIdentifier[artifactId]; + + if (!fleetArtifact) { + artifactsNotCreated.push(artifactId); + + return; + } - if (!fleetArtifact) return; newManifest.replaceArtifact(fleetArtifact); - this.logger.debug(`New created artifact ${artifactId} added to the manifest`); + newArtifactsAddedToManifest.push(artifactId); }); } + if (artifactsNotCreated.length) { + this.logger.debug( + `A total of [${ + artifactsNotCreated.length + }] artifacts were not created. Prior version of the artifact will remain in manifest.\n${artifactsNotCreated.join( + '\n' + )}` + ); + } + + if (newArtifactsAddedToManifest.length !== 0) { + this.logger.debug( + `Newly created artifacts added to the manifest:\n${newArtifactsAddedToManifest.join('\n')}` + ); + } + return errors; } @@ -469,15 +482,24 @@ export class ManifestManager { if (isEmpty(artifactIds)) { return []; } + const errors = await this.artifactClient.bulkDeleteArtifacts(artifactIds); + if (!isEmpty(errors)) { return errors; } - for (const artifactId of artifactIds) { - this.logger.info(`Cleaned up artifact ${artifactId}`); + + this.logger.info(`Count of cleaned up artifacts: ${artifactIds.length}`); + + if (artifactIds.length !== 0) { + this.logger.debug(`Deleted artifacts from cleanup:\n${artifactIds.join('\n ')}`); } + return []; } catch (err) { + this.logger.error( + `Attempted to delete [${artifactIds.length}] outdated artifacts failed with: ${err.message}\n${err.stack}` + ); return [err]; } } @@ -508,22 +530,35 @@ export class ManifestManager { const fleetArtifacts = await this.listAllArtifacts(); const fleetArtifactsById = keyBy(fleetArtifacts, (artifact) => getArtifactId(artifact)); + const invalidArtifactIds: string[] = []; + // Ensure that all artifacts currently defined in the Manifest have a valid artifact in fleet, + // and remove any that does not have an actual artifact from the manifest for (const entry of manifestSo.attributes.artifacts) { const artifact = fleetArtifactsById[entry.artifactId]; if (!artifact) { - this.logger.error( - new InvalidInternalManifestError(`artifact id [${entry.artifactId}] not found!`, { - entry, - action: 'removed from internal ManifestManger tracking map', - }) - ); + invalidArtifactIds.push(entry.artifactId); } else { manifest.addEntry(artifact, entry.policyId); } } + if (invalidArtifactIds.length) { + this.logger.warn( + `Missing artifacts detected! Internal artifact manifest (SavedObject version [${ + manifestSo.version + }]) references [${ + invalidArtifactIds.length + }] artifact IDs that don't exist.\nFirst 10 below (run with logging set to 'debug' to see all):\n${invalidArtifactIds + .slice(0, 10) + .join('\n')}` + ); + this.logger.debug( + `Artifact ID references that are missing:\n${stringify(invalidArtifactIds)}` + ); + } + return manifest; } catch (error) { if (!error.output || error.output.statusCode !== 404) { @@ -569,15 +604,10 @@ export class ManifestManager { for (const result of results) { iterateArtifactsBuildResult(result, (artifact, policyId) => { - const artifactToAdd = baselineManifest.getArtifact(getArtifactId(artifact)) || artifact; - if (!internalArtifactCompleteSchema.is(artifactToAdd)) { - throw new EndpointError( - `Incomplete artifact detected: ${getArtifactId(artifactToAdd)}`, - artifactToAdd - ); - } - - manifest.addEntry(artifactToAdd, policyId); + manifest.addEntry( + baselineManifest.getArtifact(getArtifactId(artifact)) || artifact, + policyId + ); }); } @@ -592,81 +622,93 @@ export class ManifestManager { * @returns {Promise} Any errors encountered. */ public async tryDispatch(manifest: Manifest): Promise { - const allPackagePolicies: PackagePolicy[] = []; - await iterateAllListItems( - (page, perPage) => this.listEndpointPolicies(page, perPage), - (packagePoliciesBatch) => { - allPackagePolicies.push(...packagePoliciesBatch); - } - ); + const errors: Error[] = []; + const updatedPolicies: string[] = []; + const unChangedPolicies: string[] = []; + const manifestVersion = manifest.getSemanticVersion(); + const execId = Math.random().toString(32).substring(3, 8); + const policyUpdateBatchProcessor = new QueueProcessor({ + batchSize: this.packagerTaskPackagePolicyUpdateBatchSize, + logger: this.logger, + key: `tryDispatch.${execId}`, + batchHandler: async ({ data: currentBatch }) => { + const response = await this.packagePolicyService.bulkUpdate( + this.savedObjectsClient, + this.esClient, + currentBatch + ); - const packagePoliciesToUpdate: PackagePolicy[] = []; + if (!isEmpty(response.failedPolicies)) { + errors.push( + ...response.failedPolicies.map((failedPolicy) => { + if (failedPolicy.error instanceof Error) { + return failedPolicy.error; + } else { + return new Error(failedPolicy.error.message); + } + }) + ); + } - const errors: Error[] = []; - allPackagePolicies.forEach((packagePolicy) => { - const { id } = packagePolicy; - if (packagePolicy.inputs.length > 0 && packagePolicy.inputs[0].config !== undefined) { - const oldManifest = packagePolicy.inputs[0].config.artifact_manifest ?? { - value: {}, - }; - - const newManifestVersion = manifest.getSemanticVersion(); - if (semver.gt(newManifestVersion, oldManifest.value.manifest_version)) { - const serializedManifest = manifest.toPackagePolicyManifest(id); - - if (!manifestDispatchSchema.is(serializedManifest)) { - errors.push(new EndpointError(`Invalid manifest for policy ${id}`, serializedManifest)); - } else if (!manifestsEqual(serializedManifest, oldManifest.value)) { - packagePolicy.inputs[0].config.artifact_manifest = { value: serializedManifest }; - packagePoliciesToUpdate.push(packagePolicy); + if (response.updatedPolicies) { + updatedPolicies.push( + ...response.updatedPolicies.map((policy) => { + return `[${policy.id}][${policy.name}] updated with manifest version: [${manifestVersion}]`; + }) + ); + } + }, + }); + + for await (const policies of this.fetchAllPolicies()) { + for (const packagePolicy of policies) { + const { id, name } = packagePolicy; + + if (packagePolicy.inputs.length > 0 && packagePolicy.inputs[0].config !== undefined) { + const oldManifest = packagePolicy.inputs[0].config.artifact_manifest ?? { + value: {}, + }; + + const newManifestVersion = manifest.getSemanticVersion(); + + if (semver.gt(newManifestVersion, oldManifest.value.manifest_version)) { + const serializedManifest = manifest.toPackagePolicyManifest(id); + + if (!manifestDispatchSchema.is(serializedManifest)) { + errors.push( + new EndpointError(`Invalid manifest for policy ${id}`, serializedManifest) + ); + } else if (!manifestsEqual(serializedManifest, oldManifest.value)) { + packagePolicy.inputs[0].config.artifact_manifest = { value: serializedManifest }; + policyUpdateBatchProcessor.addToQueue(packagePolicy); + } else { + unChangedPolicies.push(`[${id}][${name}] No change in manifest content`); + } } else { - this.logger.debug( - `No change in manifest content for package policy: ${id}. Staying on old version` - ); + unChangedPolicies.push(`[${id}][${name}] No change in manifest version`); } } else { - this.logger.debug(`No change in manifest version for package policy: ${id}`); + errors.push( + new EndpointError(`Package Policy ${id} has no 'inputs[0].config'`, packagePolicy) + ); } - } else { - errors.push( - new EndpointError(`Package Policy ${id} has no 'inputs[0].config'`, packagePolicy) - ); } - }); + } - // Split updates in batches with batch size: packagerTaskPackagePolicyUpdateBatchSize - const updateBatches = chunk( - packagePoliciesToUpdate, - this.packagerTaskPackagePolicyUpdateBatchSize + await policyUpdateBatchProcessor.complete(); + + this.logger.info( + `Processed [${updatedPolicies.length + unChangedPolicies.length}] Policies: updated: [${ + updatedPolicies.length + }], un-changed: [${unChangedPolicies.length}]` ); - for (const currentBatch of updateBatches) { - const response = await this.packagePolicyService.bulkUpdate( - this.savedObjectsClient, - this.esClient, - currentBatch - ); + if (updatedPolicies.length) { + this.logger.debug(`Updated Policies:\n ${updatedPolicies.join('\n ')}`); + } - // Update errors - if (!isEmpty(response.failedPolicies)) { - errors.push( - ...response.failedPolicies.map((failedPolicy) => { - if (failedPolicy.error instanceof Error) { - return failedPolicy.error; - } else { - return new Error(failedPolicy.error.message); - } - }) - ); - } - // Log success updates - for (const updatedPolicy of response.updatedPolicies || []) { - this.logger.debug( - `Updated package policy ${ - updatedPolicy.id - } with manifest version ${manifest.getSemanticVersion()}` - ); - } + if (unChangedPolicies.length) { + this.logger.debug(`Un-changed Policies:\n ${unChangedPolicies.join('\n ')}`); } return errors; @@ -696,31 +738,24 @@ export class ManifestManager { this.logger.info(`Committed manifest ${manifest.getSemanticVersion()}`); } - private async listEndpointPolicies( - page: number, - perPage: number - ): Promise> { - return this.packagePolicyService.list(this.savedObjectsClient, { - page, - perPage, + private fetchAllPolicies(): AsyncIterable { + return this.packagePolicyService.fetchAllItems(this.savedObjectsClient, { kuery: 'ingest-package-policies.package.name:endpoint', }); } private async listEndpointPolicyIds(): Promise { const allPolicyIds: string[] = []; - await iterateAllListItems( - (page, perPage) => { - return this.packagePolicyService.listIds(this.savedObjectsClient, { - page, - perPage, - kuery: 'ingest-package-policies.package.name:endpoint', - }); - }, - (packagePolicyIdsBatch) => { - allPolicyIds.push(...packagePolicyIdsBatch); - } - ); + const idFetcher = this.packagePolicyService.fetchAllItemIds(this.savedObjectsClient, { + kuery: 'ingest-package-policies.package.name:endpoint', + }); + + for await (const itemIds of idFetcher) { + allPolicyIds.push(...itemIds); + } + + this.logger.debug(`Retrieved [${allPolicyIds.length}] endpoint integration policy IDs`); + return allPolicyIds; } @@ -733,70 +768,68 @@ export class ManifestManager { * @returns Artifact[] */ private async listAllArtifacts(): Promise { - const fleetArtifacts = []; - const perPage = 100; - let page = 1; + const fleetArtifacts: Artifact[] = []; + let total = 0; - let fleetArtifactsResponse = await this.artifactClient.listArtifacts({ - perPage, - page, - }); - fleetArtifacts.push(...fleetArtifactsResponse.items); - - while ( - fleetArtifactsResponse.total > fleetArtifacts.length && - !isEmpty(fleetArtifactsResponse.items) - ) { - page += 1; - fleetArtifactsResponse = await this.artifactClient.listArtifacts({ - perPage, - page, - }); - fleetArtifacts.push(...fleetArtifactsResponse.items); + for await (const artifacts of this.artifactClient.fetchAll()) { + fleetArtifacts.push(...artifacts); + total += artifacts.length; } + + this.logger.info(`Count of current stored artifacts: ${total}`); + return fleetArtifacts; } /** - * Cleanup .fleet-artifacts index if there are some orphan artifacts + * Pulls in all artifacts from Fleet and checks to ensure they are all being referenced + * by the Manifest. If any are found to not be in the current Manifest (orphan), they + * are cleaned up (deleted) */ public async cleanup(manifest: Manifest) { - try { - const fleetArtifacts = await this.listAllArtifacts(); - if (isEmpty(fleetArtifacts)) { - return; - } - - const badArtifacts = []; - const badArtifactIds = []; + const badArtifactIds: string[] = []; + const errors: string[] = []; + const artifactDeletionProcess = new QueueProcessor({ + batchSize: this.packagerTaskPackagePolicyUpdateBatchSize, + logger: this.logger, + key: 'cleanup', + batchHandler: async ({ batch, data }) => { + const deleteErrors = await this.artifactClient.bulkDeleteArtifacts(data); + + badArtifactIds.push(...data); + + if (deleteErrors.length) { + errors.push( + `Delete batch #[${batch}] with [${data.length}] items:\n${stringify(deleteErrors)}` + ); + } + }, + }); - const manifestArtifactsIds = manifest - .getAllArtifacts() - .map((artifact) => getArtifactId(artifact)); + const validArtifactIds = manifest.getAllArtifacts().map((artifact) => getArtifactId(artifact)); - for (const fleetArtifact of fleetArtifacts) { - const artifactId = getArtifactId(fleetArtifact); - const isArtifactInManifest = manifestArtifactsIds.includes(artifactId); + for await (const artifacts of this.artifactClient.fetchAll()) { + for (const artifact of artifacts) { + const artifactId = getArtifactId(artifact); + const isArtifactInManifest = validArtifactIds.includes(artifactId); if (!isArtifactInManifest) { - badArtifacts.push(fleetArtifact); - badArtifactIds.push(artifactId); + artifactDeletionProcess.addToQueue(artifactId); } } + } - if (isEmpty(badArtifacts)) { - return; - } + await artifactDeletionProcess.complete(); + if (errors.length > 0) { this.logger.error( - new EndpointError(`Cleaning up ${badArtifacts.length} orphan artifacts`, badArtifacts) + `The following errors were encountered while attempting to delete [${ + badArtifactIds.length + }] orphaned artifacts:\n${stringify(errors)}` ); - - await this.artifactClient.bulkDeleteArtifacts(badArtifactIds); - - this.logger.info(`All orphan artifacts has been removed successfully`); - } catch (error) { - this.logger.error(new EndpointError('There was an error cleaning orphan artifacts', error)); + } else if (badArtifactIds.length > 0) { + this.logger.info(`Count of orphan artifacts cleaned up: ${badArtifactIds.length}`); + this.logger.debug(`Orphan artifacts deleted from Fleet:\n${stringify(badArtifactIds)}`); } } } diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/mocks.ts index 2acfa7b7b4794..1a1dd701e9803 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/mocks.ts @@ -65,6 +65,7 @@ export const createEndpointArtifactClientMock = ( bulkDeleteArtifacts: jest.fn(async (...args) => endpointArtifactClientMocked.bulkDeleteArtifacts(...args) ), + fetchAll: jest.fn((...args) => endpointArtifactClientMocked.fetchAll(...args)), _esClient: esClient, }; }; diff --git a/x-pack/plugins/security_solution/server/endpoint/utils/queue_processor.ts b/x-pack/plugins/security_solution/server/endpoint/utils/queue_processor.ts new file mode 100644 index 0000000000000..f4f3e4ac76852 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/utils/queue_processor.ts @@ -0,0 +1,152 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Logger } from '@kbn/core/server'; + +export interface QueueProcessorOptions { + batchHandler: (batch: { batch: number; data: T[] }) => Promise; + batchSize?: number; + logger?: Logger; + /** + * Used when `logger` is passed. It will be used to define the logging messages context path. + * Defaults to the name of the callback provided in `batchHandler` + */ + key?: string; +} + +/** + * Process an un-bound amount of items in batches. Each batch is process once the queued reach the + * `batchSize`, thus processing is gradually executed ensuring that data is not held in memory + * for too long. Once all items are added to the Queue, calling + * `.complete()` will ensure they are all processed. + * + * @example + * const processor = new QueueProcessor<{ id: string }>({ + * batchHandler: ({ data, batch }) => { + * // data === array of `{ id: string }` + * // batch === batch number + * } + * }); + * + * const myIdList = [ .... ]; // Array with 50 string + * + * for (const id of myIdList) { + * batchHandler.addToQueue({ id: id}); + * } + * + * await processor.complete(); + */ +export class QueueProcessor { + private readonly batchSize: number; + private readonly batchHandler: QueueProcessorOptions['batchHandler']; + private readonly logger: Logger | undefined = undefined; + + private queue: T[] = []; + private processingPromise: Promise | undefined = undefined; + private batchCount = 0; + private itemsProcessedCount = 0; + + constructor({ + batchHandler, + batchSize = 10, + logger, + key = 'QueueProcessor', + }: QueueProcessorOptions) { + if (batchSize < 1 || !Number.isFinite(batchSize)) { + throw new Error(`batchSize must be a number greater than zero`); + } + + this.batchSize = batchSize; + this.batchHandler = batchHandler; + this.logger = logger ? logger.get(key) : undefined; + } + + protected log( + message: string, + output: keyof Pick = 'info' + ): void { + if (this.logger) { + this.logger[output](message); + } + } + + protected async processQueue(all: boolean = false) { + if (this.processingPromise || this.queue.length === 0) { + return; + } + + const runThroughQueue = async () => { + let hasMoreData = true; + + while (hasMoreData) { + try { + if (all || this.queue.length >= this.batchSize) { + const batchPage = this.queue.splice(0, this.batchSize); + const batchPageSize = batchPage.length; + const remainingItemsSize = this.queue.length; + + hasMoreData = (all && remainingItemsSize > 0) || remainingItemsSize >= this.batchSize; + this.itemsProcessedCount += batchPageSize; + this.batchCount++; + + try { + this.log( + `Processing batch [${this.batchCount}] with [${batchPageSize}] items. Items remaining in queue: [${remainingItemsSize}]`, + 'debug' + ); + await this.batchHandler({ batch: this.batchCount, data: batchPage }); + } catch (err) { + this.log( + `batchHandler threw error (below). Will continue on to next batch page:\n${err}`, + 'debug' + ); + // ignore errors in the batch page processing and keep going to process others. + // callback should have handled errors that its process might throw + } + } else { + hasMoreData = false; + } + } catch (err) { + hasMoreData = false; + throw err; + } + } + }; + + this.processingPromise = runThroughQueue().finally(() => { + this.processingPromise = undefined; + }); + + return this.processingPromise; + } + + /** + * Adds an update to the queue + */ + public addToQueue(...data: T[]) { + this.queue.push(...data); + this.processQueue(); + } + + /** + * Flushes the queue and awaits processing of all remaining updates. + * + * **IMPORTANT**: Always make sure `complete()` is called to ensure no items are left in the queue + */ + public async complete(): Promise { + if (this.processingPromise) { + await this.processingPromise.finally(() => {}); + } + + await this.processQueue(true); + + this.log( + `Processed [${this.batchCount}] batches and a total of [${this.itemsProcessedCount}] items`, + 'debug' + ); + } +} diff --git a/x-pack/plugins/security_solution/server/endpoint/utils/translations.ts b/x-pack/plugins/security_solution/server/endpoint/utils/translations.ts new file mode 100644 index 0000000000000..1b904bf49af15 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/utils/translations.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const EMPTY_COMMENT = i18n.translate( + 'xpack.securitySolution.endpoint.updateCases.emptyComment', + { + defaultMessage: 'No comment provided', + } +); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts index f45f005c7c34b..5abdc9b4904e4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts @@ -181,11 +181,12 @@ describe('Create rule route', () => { // @ts-expect-error We're writting to a read only property just for the purpose of the test clients.config.experimentalFeatures.endpointResponseActionsEnabled = true; }); - const getResponseAction = (command: string = 'isolate') => ({ + const getResponseAction = (command: string = 'isolate', config?: object) => ({ action_type_id: '.endpoint', params: { command, comment: '', + ...(config ? { config } : {}), }, }); const defaultAction = getResponseAction(); @@ -224,8 +225,38 @@ describe('Create rule route', () => { 'User is not authorized to change isolate response actions' ); }); + test('pass when provided with process action', async () => { + const processAction = getResponseAction('kill-process', { overwrite: true, field: '' }); + + const request = requestMock.create({ + method: 'post', + path: DETECTION_ENGINE_RULES_URL, + body: { + ...getCreateRulesSchemaMock(), + response_actions: [processAction], + }, + }); + const result = await server.validate(request); + expect(result.badRequest).not.toHaveBeenCalled(); + }); test('fails when provided with an unsupported command', async () => { - const wrongAction = getResponseAction('processes'); + const wrongAction = getResponseAction('execute'); + + const request = requestMock.create({ + method: 'post', + path: DETECTION_ENGINE_RULES_URL, + body: { + ...getCreateRulesSchemaMock(), + response_actions: [wrongAction], + }, + }); + const result = await server.validate(request); + expect(result.badRequest).toHaveBeenCalledWith( + `response_actions.0.action_type_id: Invalid literal value, expected \".osquery\", response_actions.0.params.command: Invalid literal value, expected \"isolate\", response_actions.0.params.command: Invalid enum value. Expected 'kill-process' | 'suspend-process', received 'execute', response_actions.0.params.config: Required` + ); + }); + test('fails when provided with payload missing data', async () => { + const wrongAction = getResponseAction('kill-process', { overwrite: true }); const request = requestMock.create({ method: 'post', @@ -237,7 +268,7 @@ describe('Create rule route', () => { }); const result = await server.validate(request); expect(result.badRequest).toHaveBeenCalledWith( - 'response_actions.0.action_type_id: Invalid literal value, expected ".osquery", response_actions.0.params.command: Invalid literal value, expected "isolate"' + `response_actions.0.action_type_id: Invalid literal value, expected \".osquery\", response_actions.0.params.command: Invalid literal value, expected \"isolate\", response_actions.0.params.config.field: Required` ); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts index 6bdafa76fd9b6..4eea0dc1797d5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts @@ -23,7 +23,7 @@ import { getUpdateRulesSchemaMock, } from '../../../../../../../common/api/detection_engine/model/rule_schema/mocks'; import { getQueryRuleParams } from '../../../../rule_schema/mocks'; -import { ResponseActionTypesEnum } from '../../../../../../../common/api/detection_engine/model/rule_response_actions'; +import { ResponseActionTypesEnum } from '../../../../../../../common/api/detection_engine'; jest.mock('../../../../../machine_learning/authz'); @@ -189,11 +189,12 @@ describe('Update rule route', () => { // @ts-expect-error We're writting to a read only property just for the purpose of the test clients.config.experimentalFeatures.endpointResponseActionsEnabled = true; }); - const getResponseAction = (command: string = 'isolate') => ({ + const getResponseAction = (command: string = 'isolate', config?: object) => ({ action_type_id: '.endpoint', params: { command, comment: '', + ...(config ? { config } : {}), }, }); const defaultAction = getResponseAction(); @@ -249,6 +250,7 @@ describe('Update rule route', () => { params: { command: 'isolate', comment: '', + config: undefined, }, }, ], @@ -272,7 +274,7 @@ describe('Update rule route', () => { ); }); test('fails when provided with an unsupported command', async () => { - const wrongAction = getResponseAction('processes'); + const wrongAction = getResponseAction('execute'); const request = requestMock.create({ method: 'post', @@ -284,7 +286,23 @@ describe('Update rule route', () => { }); const result = await server.validate(request); expect(result.badRequest).toHaveBeenCalledWith( - `response_actions.0.action_type_id: Invalid literal value, expected \".osquery\", response_actions.0.params.command: Invalid literal value, expected \"isolate\"` + `response_actions.0.action_type_id: Invalid literal value, expected \".osquery\", response_actions.0.params.command: Invalid literal value, expected \"isolate\", response_actions.0.params.command: Invalid enum value. Expected 'kill-process' | 'suspend-process', received 'execute', response_actions.0.params.config: Required` + ); + }); + test('fails when provided with payload missing data', async () => { + const wrongAction = getResponseAction('kill-process', { overwrite: true }); + + const request = requestMock.create({ + method: 'post', + path: DETECTION_ENGINE_RULES_URL, + body: { + ...getCreateRulesSchemaMock(), + response_actions: [wrongAction], + }, + }); + const result = await server.validate(request); + expect(result.badRequest).toHaveBeenCalledWith( + `response_actions.0.action_type_id: Invalid literal value, expected \".osquery\", response_actions.0.params.command: Invalid literal value, expected \"isolate\", response_actions.0.params.config.field: Required` ); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/endpoint_params_type_guards.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/endpoint_params_type_guards.ts new file mode 100644 index 0000000000000..d1dbb9c7d4958 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/endpoint_params_type_guards.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + DefaultParams, + ProcessesParams, + RuleResponseEndpointAction, +} from '../../../../common/api/detection_engine'; + +export const isIsolateAction = ( + params: RuleResponseEndpointAction['params'] +): params is DefaultParams => { + return params.command === 'isolate'; +}; + +export const isProcessesAction = ( + params: RuleResponseEndpointAction['params'] +): params is ProcessesParams => { + return params.command === 'kill-process' || params.command === 'suspend-process'; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/endpoint_response_action.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/endpoint_response_action.ts index f6c3161921934..b6aa53bb32ff6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/endpoint_response_action.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/endpoint_response_action.ts @@ -5,48 +5,81 @@ * 2.0. */ -import { each, map, uniq } from 'lodash'; import { ALERT_RULE_NAME, ALERT_RULE_UUID } from '@kbn/rule-data-utils'; -import type { ResponseActionAlerts } from './types'; +import { each } from 'lodash'; + +import type { ExperimentalFeatures } from '../../../../common'; +import { isIsolateAction, isProcessesAction } from './endpoint_params_type_guards'; +import type { RuleResponseEndpointAction } from '../../../../common/api/detection_engine'; import type { EndpointAppContextService } from '../../../endpoint/endpoint_app_context_services'; -import type { RuleResponseEndpointAction } from '../../../../common/api/detection_engine/model/rule_response_actions'; +import { getProcessAlerts, getIsolateAlerts, getErrorProcessAlerts } from './utils'; + +import type { ResponseActionAlerts, AlertsAction } from './types'; export const endpointResponseAction = ( responseAction: RuleResponseEndpointAction, endpointAppContextService: EndpointAppContextService, - { alerts }: ResponseActionAlerts + { alerts }: ResponseActionAlerts, + experimentalFeatures: ExperimentalFeatures ) => { const { comment, command } = responseAction.params; + const commonData = { comment, command, rule_id: alerts[0][ALERT_RULE_UUID], rule_name: alerts[0][ALERT_RULE_NAME], + agent_type: 'endpoint' as const, }; - const agentIds = uniq(map(alerts, 'agent.id')); - const alertIds = map(alerts, '_id'); - const hosts = alerts.reduce>((acc, alert) => { - if (alert.agent?.name && !acc[alert.agent.id]) { - acc[alert.agent.id] = alert.agent.name; - } - return acc; - }, {}); - return Promise.all( - each(agentIds, async (agent) => - endpointAppContextService.getActionCreateService().createActionFromAlert( + if (isIsolateAction(responseAction.params)) { + const alertsPerAgent = getIsolateAlerts(alerts); + each(alertsPerAgent, (actionPayload) => { + return endpointAppContextService.getActionCreateService().createActionFromAlert( { - hosts: { - [agent]: { - name: hosts[agent], - }, - }, - endpoint_ids: [agent], - alert_ids: alertIds, + ...actionPayload, ...commonData, }, - [agent] - ) - ) - ); + actionPayload.endpoint_ids + ); + }); + } + + const automatedProcessActionsEnabled = experimentalFeatures?.automatedProcessActionsEnabled; + + if (automatedProcessActionsEnabled) { + const createProcessActionFromAlerts = ( + actionAlerts: Record> + ) => { + const createAction = async (alert: AlertsAction) => { + const { hosts, parameters, error } = alert; + + const actionData = { + hosts, + endpoint_ids: alert.endpoint_ids, + alert_ids: alert.alert_ids, + error, + parameters, + ...commonData, + }; + + return endpointAppContextService + .getActionCreateService() + .createActionFromAlert(actionData, alert.endpoint_ids); + }; + return each(actionAlerts, (actionPerAgent) => { + return each(actionPerAgent, createAction); + }); + }; + + if (isProcessesAction(responseAction.params)) { + const foundFields = getProcessAlerts(alerts, responseAction.params.config); + const notFoundField = getErrorProcessAlerts(alerts, responseAction.params.config); + + const processActions = createProcessActionFromAlerts(foundFields); + const processActionsWithError = createProcessActionFromAlerts(notFoundField); + + return Promise.all([processActions, processActionsWithError]); + } + } }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/schedule_notification_response_actions.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/schedule_notification_response_actions.test.ts index 672434bfc94d0..927ba3cbe4a46 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/schedule_notification_response_actions.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/schedule_notification_response_actions.test.ts @@ -6,105 +6,213 @@ */ import { getScheduleNotificationResponseActionsService } from './schedule_notification_response_actions'; -import type { RuleResponseAction } from '../../../../common/api/detection_engine/model/rule_response_actions'; -import { ResponseActionTypesEnum } from '../../../../common/api/detection_engine/model/rule_response_actions'; +import type { RuleResponseAction } from '../../../../common/api/detection_engine'; +import { ResponseActionTypesEnum } from '../../../../common/api/detection_engine'; +import { ALERT_RULE_NAME, ALERT_RULE_UUID } from '@kbn/rule-data-utils'; describe('ScheduleNotificationResponseActions', () => { - const signalOne = { agent: { id: 'agent-id-1' }, _id: 'alert-id-1', user: { id: 'S-1-5-20' } }; - const signalTwo = { agent: { id: 'agent-id-2' }, _id: 'alert-id-2' }; - const signals = [signalOne, signalTwo]; - const defaultQueryParams = { - ecsMapping: { testField: { field: 'testField', value: 'testValue' } }, - savedQueryId: 'testSavedQueryId', - query: undefined, - queries: [], - packId: undefined, - }; - const defaultPackParams = { - packId: 'testPackId', - queries: [], - query: undefined, - ecsMapping: { testField: { field: 'testField', value: 'testValue' } }, - savedQueryId: undefined, - }; - const defaultQueries = { - ecs_mapping: undefined, - platform: 'windows', - version: '1.0.0', - snapshot: true, - removed: false, + const signalOne = { + agent: { id: 'agent-id-1' }, + _id: 'alert-id-1', + user: { id: 'S-1-5-20' }, + process: { + pid: 123, + }, + [ALERT_RULE_UUID]: 'rule-id-1', + [ALERT_RULE_NAME]: 'rule-name-1', }; + const signalTwo = { agent: { id: 'agent-id-2' }, _id: 'alert-id-2' }; + const getSignals = () => [signalOne, signalTwo]; - const defaultResultParams = { - agent_ids: ['agent-id-1', 'agent-id-2'], - alert_ids: ['alert-id-1', 'alert-id-2'], - }; - const defaultQueryResultParams = { - ...defaultResultParams, - ecs_mapping: { testField: { field: 'testField', value: 'testValue' } }, - ecsMapping: undefined, - saved_query_id: 'testSavedQueryId', - savedQueryId: undefined, - queries: [], - }; - const defaultPackResultParams = { - ...defaultResultParams, - query: undefined, - saved_query_id: undefined, - ecs_mapping: { testField: { field: 'testField', value: 'testValue' } }, - }; const osqueryActionMock = { create: jest.fn(), stop: jest.fn(), }; - const endpointActionMock = jest.fn(); - + const endpointActionMock = { + getActionCreateService: jest.fn().mockReturnValue({ + createActionFromAlert: jest.fn(), + }), + }; const scheduleNotificationResponseActions = getScheduleNotificationResponseActionsService({ osqueryCreateActionService: osqueryActionMock, endpointAppContextService: endpointActionMock as never, + experimentalFeatures: { + automatedProcessActionsEnabled: true, + endpointResponseActionsEnabled: true, + } as never, }); - const simpleQuery = 'select * from uptime'; - it('should handle osquery response actions with query', async () => { - const responseActions: RuleResponseAction[] = [ - { - actionTypeId: ResponseActionTypesEnum['.osquery'], - params: { - ...defaultQueryParams, - query: simpleQuery, + describe('Osquery', () => { + const simpleQuery = 'select * from uptime'; + const defaultQueryParams = { + ecsMapping: { testField: { field: 'testField', value: 'testValue' } }, + savedQueryId: 'testSavedQueryId', + query: undefined, + queries: [], + packId: undefined, + }; + const defaultPackParams = { + packId: 'testPackId', + queries: [], + query: undefined, + ecsMapping: { testField: { field: 'testField', value: 'testValue' } }, + savedQueryId: undefined, + }; + const defaultQueries = { + ecs_mapping: undefined, + platform: 'windows', + version: '1.0.0', + snapshot: true, + removed: false, + }; + + const defaultResultParams = { + agent_ids: ['agent-id-1', 'agent-id-2'], + alert_ids: ['alert-id-1', 'alert-id-2'], + }; + const defaultQueryResultParams = { + ...defaultResultParams, + ecs_mapping: { testField: { field: 'testField', value: 'testValue' } }, + ecsMapping: undefined, + saved_query_id: 'testSavedQueryId', + savedQueryId: undefined, + queries: [], + }; + const defaultPackResultParams = { + ...defaultResultParams, + query: undefined, + saved_query_id: undefined, + ecs_mapping: { testField: { field: 'testField', value: 'testValue' } }, + }; + it('should handle osquery response actions with query', async () => { + const signals = getSignals(); + const responseActions: RuleResponseAction[] = [ + { + actionTypeId: ResponseActionTypesEnum['.osquery'], + params: { + ...defaultQueryParams, + query: simpleQuery, + }, }, - }, - ]; - scheduleNotificationResponseActions({ signals, responseActions }); + ]; + scheduleNotificationResponseActions({ signals, responseActions }); - expect(osqueryActionMock.create).toHaveBeenCalledWith({ - ...defaultQueryResultParams, - query: simpleQuery, + expect(osqueryActionMock.create).toHaveBeenCalledWith({ + ...defaultQueryResultParams, + query: simpleQuery, + }); + }); + + it('should handle osquery response actions with packs', async () => { + const signals = getSignals(); + + const responseActions: RuleResponseAction[] = [ + { + actionTypeId: ResponseActionTypesEnum['.osquery'], + params: { + ...defaultPackParams, + queries: [ + { + ...defaultQueries, + id: 'query-1', + query: simpleQuery, + }, + ], + packId: 'testPackId', + }, + }, + ]; + scheduleNotificationResponseActions({ signals, responseActions }); + + expect(osqueryActionMock.create).toHaveBeenCalledWith({ + ...defaultPackResultParams, + queries: [{ ...defaultQueries, id: 'query-1', query: simpleQuery }], + }); }); - // }); - it('should handle osquery response actions with packs', async () => { - const responseActions: RuleResponseAction[] = [ - { - actionTypeId: ResponseActionTypesEnum['.osquery'], - params: { - ...defaultPackParams, - queries: [ - { - ...defaultQueries, - id: 'query-1', - query: simpleQuery, + describe('Endpoint', () => { + it('should handle endpoint isolate actions', async () => { + const signals = getSignals(); + + const responseActions: RuleResponseAction[] = [ + { + actionTypeId: ResponseActionTypesEnum['.endpoint'], + params: { + command: 'isolate', + comment: 'test isolate comment', + }, + }, + ]; + scheduleNotificationResponseActions({ signals, responseActions }); + + expect( + endpointActionMock.getActionCreateService().createActionFromAlert + ).toHaveBeenCalledTimes(signals.length); + expect( + endpointActionMock.getActionCreateService().createActionFromAlert + ).toHaveBeenCalledWith( + { + alert_ids: ['alert-id-1'], + command: 'isolate', + comment: 'test isolate comment', + endpoint_ids: ['agent-id-1'], + agent_type: 'endpoint', + hosts: { + 'agent-id-1': { + id: 'agent-id-1', + name: '', }, - ], - packId: 'testPackId', + }, + rule_id: 'rule-id-1', + rule_name: 'rule-name-1', }, - }, - ]; - scheduleNotificationResponseActions({ signals, responseActions }); + ['agent-id-1'] + ); + }); + it('should handle endpoint kill-process actions', async () => { + const signals = getSignals(); + const responseActions: RuleResponseAction[] = [ + { + actionTypeId: ResponseActionTypesEnum['.endpoint'], + params: { + command: 'kill-process', + comment: 'test process comment', + config: { + overwrite: true, + field: '', + }, + }, + }, + ]; + scheduleNotificationResponseActions({ + signals, + responseActions, + }); - expect(osqueryActionMock.create).toHaveBeenCalledWith({ - ...defaultPackResultParams, - queries: [{ ...defaultQueries, id: 'query-1', query: simpleQuery }], + expect( + endpointActionMock.getActionCreateService().createActionFromAlert + ).toHaveBeenCalledWith( + { + agent_type: 'endpoint', + alert_ids: ['alert-id-1'], + command: 'kill-process', + comment: 'test process comment', + endpoint_ids: ['agent-id-1'], + error: undefined, + hosts: { + 'agent-id-1': { + id: 'agent-id-1', + name: undefined, + }, + }, + parameters: { + pid: 123, + }, + rule_id: 'rule-id-1', + rule_name: 'rule-name-1', + }, + ['agent-id-1'] + ); }); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/schedule_notification_response_actions.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/schedule_notification_response_actions.ts index a02fafe69d8f6..f05df335c7424 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/schedule_notification_response_actions.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/schedule_notification_response_actions.ts @@ -6,23 +6,26 @@ */ import { each } from 'lodash'; +import type { ExperimentalFeatures } from '../../../../common'; import type { EndpointAppContextService } from '../../../endpoint/endpoint_app_context_services'; import type { SetupPlugins } from '../../../plugin_contract'; import { ResponseActionTypesEnum } from '../../../../common/api/detection_engine/model/rule_response_actions'; import { osqueryResponseAction } from './osquery_response_action'; import { endpointResponseAction } from './endpoint_response_action'; import type { ScheduleNotificationActions } from '../rule_types/types'; -import type { Alert, AlertWithAgent } from './types'; +import type { AlertWithAgent, Alert } from './types'; interface ScheduleNotificationResponseActionsService { endpointAppContextService: EndpointAppContextService; osqueryCreateActionService?: SetupPlugins['osquery']['createActionService']; + experimentalFeatures: ExperimentalFeatures; } export const getScheduleNotificationResponseActionsService = ({ osqueryCreateActionService, endpointAppContextService, + experimentalFeatures, }: ScheduleNotificationResponseActionsService) => ({ signals, responseActions }: ScheduleNotificationActions) => { const alerts = (signals as Alert[]).filter((alert) => alert.agent?.id) as AlertWithAgent[]; @@ -37,9 +40,14 @@ export const getScheduleNotificationResponseActionsService = }); } if (responseAction.actionTypeId === ResponseActionTypesEnum['.endpoint']) { - endpointResponseAction(responseAction, endpointAppContextService, { - alerts, - }); + endpointResponseAction( + responseAction, + endpointAppContextService, + { + alerts, + }, + experimentalFeatures + ); } }); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/types.ts index 5f1cee8d2a9ba..1ab3b8018bf1d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/types.ts @@ -6,11 +6,17 @@ */ import type { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common'; +import type { CreateActionPayload } from '../../../endpoint/services/actions/create/types'; export type Alert = ParsedTechnicalFields & { _id: string; agent?: AlertAgent; - process?: { pid: string }; + host?: { + name: string; + }; + process?: { + pid: string; + }; }; export interface AlertAgent { @@ -25,3 +31,8 @@ export interface AlertWithAgent extends Alert { export interface ResponseActionAlerts { alerts: AlertWithAgent[]; } + +export type AlertsAction = Pick< + CreateActionPayload, + 'alert_ids' | 'endpoint_ids' | 'hosts' | 'parameters' | 'error' +>; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/utils.test.ts new file mode 100644 index 0000000000000..0cb3962ab35fc --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/utils.test.ts @@ -0,0 +1,242 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getErrorProcessAlerts, getIsolateAlerts, getProcessAlerts } from './utils'; +import type { AlertWithAgent } from './types'; + +const getSampleAlerts = (): AlertWithAgent[] => { + const alert = { + _id: 'alert1', + process: { + pid: 1, + }, + agent: { + name: 'jammy-1', + id: 'agent-id-1', + }, + }; + const alert2 = { + _id: 'alert2', + process: { + pid: 2, + }, + agent: { + name: 'jammy-2', + id: 'agent-id-2', + }, + }; + const alert3 = { + _id: 'alert3', + process: { + pid: 2, + }, + agent: { + name: 'jammy-1', + id: 'agent-id-1', + }, + }; + const alert4 = { + _id: 'alert4', + agent: { + name: 'jammy-1', + id: 'agent-id-1', + }, + }; + const alert5 = { + _id: 'alert5', + process: { + entity_id: 2, + }, + agent: { + name: 'jammy-1', + id: 'agent-id-1', + }, + }; + // Casted as unknown first because we do not need all the data to test the functionality + return [alert, alert2, alert3, alert4, alert5] as unknown as AlertWithAgent[]; +}; +describe('EndpointResponseActionsUtils', () => { + describe('getIsolateAlerts', () => { + const alerts = getSampleAlerts(); + it('should return proper number of actions divided per agents with specified alert_ids', async () => { + const isolateAlerts = getIsolateAlerts(alerts); + + const result = { + 'agent-id-1': { + alert_ids: ['alert1', 'alert3', 'alert4', 'alert5'], + endpoint_ids: ['agent-id-1'], + hosts: { + 'agent-id-1': { + id: 'agent-id-1', + name: 'jammy-1', + }, + }, + }, + 'agent-id-2': { + alert_ids: ['alert2'], + endpoint_ids: ['agent-id-2'], + hosts: { + 'agent-id-2': { + id: 'agent-id-2', + name: 'jammy-2', + }, + }, + }, + }; + expect(isolateAlerts).toEqual(result); + }); + }); + describe('getProcessAlerts', () => { + const alerts = getSampleAlerts(); + + it('should return actions that are valid based on default field (pid)', async () => { + const processAlerts = getProcessAlerts(alerts, { + overwrite: true, + field: '', + }); + + const result = { + 'agent-id-1': { + '1': { + alert_ids: ['alert1'], + endpoint_ids: ['agent-id-1'], + hosts: { + 'agent-id-1': { + id: 'agent-id-1', + name: 'jammy-1', + }, + }, + parameters: { + pid: 1, + }, + }, + '2': { + alert_ids: ['alert3'], + endpoint_ids: ['agent-id-1'], + hosts: { + 'agent-id-1': { + id: 'agent-id-1', + name: 'jammy-1', + }, + }, + parameters: { + pid: 2, + }, + }, + }, + 'agent-id-2': { + '2': { + alert_ids: ['alert2'], + endpoint_ids: ['agent-id-2'], + hosts: { + 'agent-id-2': { + id: 'agent-id-2', + name: 'jammy-2', + }, + }, + parameters: { + pid: 2, + }, + }, + }, + }; + expect(processAlerts).toEqual(result); + }); + + it('should return actions that do not have value from default field (pid)', async () => { + const processAlerts = getProcessAlerts(alerts, { + overwrite: false, + field: 'process.entity_id', + }); + + const result = { + 'agent-id-1': { + '2': { + alert_ids: ['alert5'], + endpoint_ids: ['agent-id-1'], + hosts: { + 'agent-id-1': { + id: 'agent-id-1', + name: 'jammy-1', + }, + }, + parameters: { + entity_id: 2, + }, + }, + }, + }; + expect(processAlerts).toEqual(result); + }); + }); + describe('getErrorProcessAlerts', () => { + const alerts = getSampleAlerts(); + + it('should return actions that do not have value from default field (pid)', async () => { + const processAlerts = getErrorProcessAlerts(alerts, { + overwrite: true, + field: '', + }); + + const result = { + 'agent-id-1': { + 'process.pid': { + alert_ids: ['alert4', 'alert5'], + endpoint_ids: ['agent-id-1'], + error: 'process.pid', + hosts: { + 'agent-id-1': { + id: 'agent-id-1', + name: 'jammy-1', + }, + }, + parameters: {}, + }, + }, + }; + expect(processAlerts).toEqual(result); + }); + it('should return actions that do not have value from custom field name', async () => { + const processAlerts = getErrorProcessAlerts(alerts, { + overwrite: false, + field: 'process.entity_id', + }); + + const result = { + 'agent-id-1': { + 'process.entity_id': { + alert_ids: ['alert1', 'alert3', 'alert4'], + endpoint_ids: ['agent-id-1'], + error: 'process.entity_id', + hosts: { + 'agent-id-1': { + id: 'agent-id-1', + name: 'jammy-1', + }, + }, + parameters: {}, + }, + }, + 'agent-id-2': { + 'process.entity_id': { + alert_ids: ['alert2'], + endpoint_ids: ['agent-id-2'], + error: 'process.entity_id', + hosts: { + 'agent-id-2': { + id: 'agent-id-2', + name: 'jammy-2', + }, + }, + parameters: {}, + }, + }, + }; + expect(processAlerts).toEqual(result); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/utils.ts new file mode 100644 index 0000000000000..8d0c5c3ef74c9 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/utils.ts @@ -0,0 +1,123 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { get } from 'lodash'; +import type { AlertAgent, AlertWithAgent, AlertsAction } from './types'; +import type { ProcessesParams } from '../../../../common/api/detection_engine'; + +interface ProcessAlertsAcc { + [key: string]: Record; +} + +export const getProcessAlerts = ( + alerts: AlertWithAgent[], + config: ProcessesParams['config'] +): ProcessAlertsAcc => { + if (!config) { + return {}; + } + const { overwrite, field } = config; + + return alerts.reduce((acc: ProcessAlertsAcc, alert) => { + const valueFromAlert: number = overwrite ? alert.process?.pid : get(alert, field); + + if (valueFromAlert) { + const isEntityId = !overwrite && field.includes('entity_id'); + const paramKey = isEntityId ? 'entity_id' : 'pid'; + const { _id, agent } = alert; + const { id: agentId, name } = agent as AlertAgent; + const hostName = alert.host?.name; + + const currentAgent = acc[agentId]; + const currentValue = currentAgent?.[valueFromAlert]; + + return { + ...acc, + [agentId]: { + ...(currentAgent || {}), + [valueFromAlert]: { + ...(currentValue || {}), + alert_ids: [...(currentValue?.alert_ids || []), _id], + parameters: { [paramKey]: valueFromAlert }, + endpoint_ids: [agentId], + hosts: { + ...currentValue?.hosts, + [agentId]: { name: name || hostName, id: agentId }, + }, + }, + }, + }; + } + return acc; + }, {}); +}; + +export const getErrorProcessAlerts = ( + alerts: AlertWithAgent[], + config: ProcessesParams['config'] +): ProcessAlertsAcc => { + if (!config) { + return {}; + } + const { overwrite, field } = config; + + return alerts.reduce((acc: ProcessAlertsAcc, alert) => { + const valueFromAlert: number = overwrite ? alert.process?.pid : get(alert, field); + + if (!valueFromAlert) { + const { _id, agent } = alert; + const { id: agentId, name } = agent as AlertAgent; + const hostName = alert.host?.name; + + const errorField = overwrite ? 'process.pid' : field; + const currentAgent = acc[agentId]; + const currentValue = currentAgent?.[errorField]; + + return { + ...acc, + [agentId]: { + ...(currentAgent || {}), + [errorField]: { + ...(currentValue || {}), + alert_ids: [...(currentValue?.alert_ids || []), _id], + parameters: {}, + endpoint_ids: [agentId], + hosts: { + ...currentValue?.hosts, + [agentId]: { name: name || hostName || '', id: agentId }, + }, + error: errorField, + }, + }, + }; + } + return acc; + }, {}); +}; + +export const getIsolateAlerts = (alerts: AlertWithAgent[]): Record => + alerts.reduce((acc: Record, alert) => { + const { id: agentId, name: agentName } = alert.agent || {}; + + const hostName = alert.host?.name; + + return { + ...acc, + [agentId]: { + ...(acc?.[agentId] || {}), + hosts: { + ...(acc[agentId]?.hosts || {}), + [agentId]: { + name: agentName || hostName || '', + id: agentId, + }, + }, + endpoint_ids: [agentId], + alert_ids: [...(acc[agentId]?.alert_ids || []), alert._id], + }, + }; + }, {}); diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index aaa104533e080..5b8ec7edd36ab 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -46,7 +46,13 @@ import { AppClientFactory } from './client'; import type { ConfigType } from './config'; import { createConfig } from './config'; import { initUiSettings } from './ui_settings'; -import { APP_ID, APP_UI_ID, DEFAULT_ALERTS_INDEX, SERVER_APP_ID } from '../common/constants'; +import { + APP_ID, + APP_UI_ID, + CASE_ATTACHMENT_ENDPOINT_TYPE_ID, + DEFAULT_ALERTS_INDEX, + SERVER_APP_ID, +} from '../common/constants'; import { registerEndpointRoutes } from './endpoint/routes/metadata'; import { registerPolicyRoutes } from './endpoint/routes/policy'; import { registerActionRoutes } from './endpoint/routes/actions'; @@ -230,6 +236,9 @@ export class Plugin implements ISecuritySolutionPlugin { }); this.telemetryUsageCounter = plugins.usageCollection?.createUsageCounter(APP_ID); + plugins.cases.attachmentFramework.registerExternalReference({ + id: CASE_ATTACHMENT_ENDPOINT_TYPE_ID, + }); const { ruleDataService } = plugins.ruleRegistry; let ruleDataClient: IRuleDataClient | null = null; @@ -285,6 +294,7 @@ export class Plugin implements ISecuritySolutionPlugin { scheduleNotificationResponseActionsService: getScheduleNotificationResponseActionsService({ endpointAppContextService: this.endpointAppContextService, osqueryCreateActionService: plugins.osquery.createActionService, + experimentalFeatures: config.experimentalFeatures, }), }; @@ -532,7 +542,7 @@ export class Plugin implements ISecuritySolutionPlugin { artifactClient, exceptionListClient, packagePolicyService: plugins.fleet.packagePolicyService, - logger, + logger: this.pluginContext.logger.get('ManifestManager'), experimentalFeatures: config.experimentalFeatures, packagerTaskPackagePolicyUpdateBatchSize: config.packagerTaskPackagePolicyUpdateBatchSize, esClient: core.elasticsearch.client.asInternalUser, diff --git a/x-pack/plugins/security_solution/server/plugin_contract.ts b/x-pack/plugins/security_solution/server/plugin_contract.ts index 8370e405c6807..a036ef0565fe0 100644 --- a/x-pack/plugins/security_solution/server/plugin_contract.ts +++ b/x-pack/plugins/security_solution/server/plugin_contract.ts @@ -16,7 +16,7 @@ import type { PluginSetupContract as AlertingPluginSetup, PluginStartContract as AlertingPluginStart, } from '@kbn/alerting-plugin/server'; -import type { CasesStart } from '@kbn/cases-plugin/server'; +import type { CasesStart, CasesSetup } from '@kbn/cases-plugin/server'; import type { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server'; import type { IEventLogClientService, IEventLogService } from '@kbn/event-log-plugin/server'; import type { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server'; @@ -47,6 +47,7 @@ import type { ExperimentalFeatures } from '../common'; export interface SecuritySolutionPluginSetupDependencies { alerting: AlertingPluginSetup; + cases: CasesSetup; cloud: CloudSetup; data: DataPluginSetup; encryptedSavedObjects?: EncryptedSavedObjectsPluginSetup; diff --git a/x-pack/plugins/task_manager/server/polling_lifecycle.ts b/x-pack/plugins/task_manager/server/polling_lifecycle.ts index 20a63377b41e0..475895639fc1d 100644 --- a/x-pack/plugins/task_manager/server/polling_lifecycle.ts +++ b/x-pack/plugins/task_manager/server/polling_lifecycle.ts @@ -173,7 +173,7 @@ export class TaskPollingLifecycle implements ITaskEventEmitter, <, =", - "xpack.observability.customThreshold.rule.alertFlyout.error.invalidFilterQuery": "La requête de filtre n'est pas valide.", "xpack.observability.customThreshold.rule.alertFlyout.error.invalidSearchConfiguration": "La vue de données est requise.", "xpack.observability.customThreshold.rule.alertFlyout.error.metrics.aggTypeRequired": "L'agrégation est requise", "xpack.observability.customThreshold.rule.alertFlyout.error.metrics.fieldRequired": "Le champ est obligatoire", @@ -28996,8 +28993,6 @@ "xpack.observability.slo.sloEdit.calendarTimeWindow.monthly": "Mensuel", "xpack.observability.slo.sloEdit.calendarTimeWindow.weekly": "Hebdomadaire", "xpack.observability.slo.sloEdit.cancelButton": "Annuler", - "xpack.observability.slo.sloEdit.createAlert.ruleName": "Règle d'alerte de taux d'avancement du SLO", - "xpack.observability.slo.sloEdit.createAlert.title": "Créer", "xpack.observability.slo.sloEdit.createSloButton": "Créer un SLO", "xpack.observability.slo.sloEdit.customKql.indexSelection.label": "Index", "xpack.observability.slo.sloEdit.dataPreviewChart.errorMessage": "Les paramètres d'indicateur actuels ne sont pas valides", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 3c871401f9645..10bbca87a9cad 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -124,7 +124,7 @@ "charts.legend.toggleLegendButtonAriaLabel": "凡例を切り替える", "charts.legend.toggleLegendButtonTitle": "凡例を切り替える", "charts.noDataLabel": "結果が見つかりませんでした", - "charts.palettes.complimentaryLabel": "無料", + "charts.palettes.complementaryLabel": "無料", "charts.palettes.coolLabel": "Cool", "charts.palettes.customLabel": "カスタム", "charts.palettes.defaultPaletteLabel": "デフォルト", @@ -12777,7 +12777,6 @@ "xpack.enterpriseSearch.content.index.pipelines.ingestFlyout.modalBodyAPIText": "{apiIndex}以下の設定に行われた変更は参照専用です。これらの設定は、インデックスまたはパイプラインまで永続しません。", "xpack.enterpriseSearch.content.indices.configurationConnector.apiKey.description": "まず、Elasticsearch APIキーを生成します。この{apiKeyName}は、コネクターがドキュメントを作成された{indexName}インデックスにインデックスするための読み書き権限を有効にします。キーは安全な場所に保管してください。コネクターを構成するときに必要になります。", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.connectorConnected": "コネクター{name}は、正常にSearchに接続されました。", - "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.description.secondParagraph": "コネクターリポジトリには複数の{link}が含まれています。当社のフレームワークを使用すると、カスタムデータソース用のコネクターの開発を加速できます。", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.description.thirdParagraph": "このステップでは、リポジトリを複製またはフォークし、生成されたAPIキーとコネクターIDを、関連付けられた{link}にコピーする必要があります。コネクターIDは、Searchに対するこのコネクターを特定します。サービスタイプは、コネクターが構成されているデータソースのタイプを決定します。", "xpack.enterpriseSearch.content.indices.configurationConnector.nativeConnector.config.sourceSecurityDocumentationLinkLabel": "{name}認証", "xpack.enterpriseSearch.content.indices.configurationConnector.nativeConnector.connectorConnected": "コネクター{name}は、正常にSearchに接続されました。", @@ -14086,7 +14085,6 @@ "xpack.enterpriseSearch.content.indices.configurationConnector.apiKey.confirmModal.description": "新しいAPIキーを生成すると、前のキーが無効になります。新しいAPIキーを生成しますか?この操作は元に戻せません。", "xpack.enterpriseSearch.content.indices.configurationConnector.apiKey.confirmModal.title": "Elasticsearch APIキーを生成", "xpack.enterpriseSearch.content.indices.configurationConnector.configuration.successToast.title": "構成が更新されました", - "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.clientExamplesLink": "コネクタークライアントの例", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.configurationFileLink": "構成ファイル", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.connectorDeployedText": "構成したら、インフラでコネクターをデプロイします。", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.waitingForConnector.button.label": "今すぐ再確認", @@ -28660,7 +28658,6 @@ "xpack.observability.customThreshold.rule.alertFlyout.dataViewError.noTimestamp": "選択したデータビューにタイムスタンプフィールドがありません。他のデータビューを選択してください。", "xpack.observability.customThreshold.rule.alertFlyout.defineTextQueryPrompt": "クエリフィルターを定義(任意)", "xpack.observability.customThreshold.rule.alertFlyout.error.equation.invalidCharacters": "等式フィールドでは次の文字のみを使用できます:A-Z、+、-、/、*、(、)、?、!、&、:、|、>、<、=", - "xpack.observability.customThreshold.rule.alertFlyout.error.invalidFilterQuery": "フィルタークエリは無効です。", "xpack.observability.customThreshold.rule.alertFlyout.error.invalidSearchConfiguration": "データビューが必要です。", "xpack.observability.customThreshold.rule.alertFlyout.error.metrics.aggTypeRequired": "集約が必要です", "xpack.observability.customThreshold.rule.alertFlyout.error.metrics.fieldRequired": "フィールドが必要です", @@ -28997,8 +28994,6 @@ "xpack.observability.slo.sloEdit.calendarTimeWindow.monthly": "月ごと", "xpack.observability.slo.sloEdit.calendarTimeWindow.weekly": "週ごと", "xpack.observability.slo.sloEdit.cancelButton": "キャンセル", - "xpack.observability.slo.sloEdit.createAlert.ruleName": "SLOバーンレートアラートルール", - "xpack.observability.slo.sloEdit.createAlert.title": "作成", "xpack.observability.slo.sloEdit.createSloButton": "SLOの作成", "xpack.observability.slo.sloEdit.customKql.indexSelection.label": "インデックス", "xpack.observability.slo.sloEdit.dataPreviewChart.errorMessage": "現在のインジケーター設定は無効です", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index f1ba72252316c..bd37c0d9f3fb6 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -124,7 +124,7 @@ "charts.legend.toggleLegendButtonAriaLabel": "切换图例", "charts.legend.toggleLegendButtonTitle": "切换图例", "charts.noDataLabel": "找不到结果", - "charts.palettes.complimentaryLabel": "免费", + "charts.palettes.complementaryLabel": "免费", "charts.palettes.coolLabel": "冷", "charts.palettes.customLabel": "定制", "charts.palettes.defaultPaletteLabel": "默认", @@ -12871,7 +12871,6 @@ "xpack.enterpriseSearch.content.index.pipelines.ingestFlyout.modalBodyAPIText": "{apiIndex}对以下设置所做的更改仅供参考。这些设置不会持续用于您的索引或管道。", "xpack.enterpriseSearch.content.indices.configurationConnector.apiKey.description": "首先,生成一个 Elasticsearch API 密钥。此 {apiKeyName} 密钥将为连接器启用读取和写入权限,以便将文档索引到已创建的 {indexName} 索引。请将该密钥保存到安全位置,因为您需要它来配置连接器。", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.connectorConnected": "您的连接器 {name} 已成功连接到 Search。", - "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.description.secondParagraph": "连接器存储库包含几个 {link}。使用我们的框架可加速为定制数据源开发连接器。", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.description.thirdParagraph": "在此步骤中,您需要克隆或分叉存储库,然后将生成的 API 密钥和连接器 ID 复制到关联的 {link}。连接器 ID 会将此连接器标识到 Search。此服务类型将决定要将连接器配置用于哪些类型的数据源。", "xpack.enterpriseSearch.content.indices.configurationConnector.nativeConnector.config.sourceSecurityDocumentationLinkLabel": "{name} 身份验证", "xpack.enterpriseSearch.content.indices.configurationConnector.nativeConnector.connectorConnected": "您的连接器 {name} 已成功连接到 Search。", @@ -14180,7 +14179,6 @@ "xpack.enterpriseSearch.content.indices.configurationConnector.apiKey.confirmModal.description": "生成新的 API 密钥将使之前的密钥失效。是否确定要生成新的 API 密钥?此操作无法撤消。", "xpack.enterpriseSearch.content.indices.configurationConnector.apiKey.confirmModal.title": "生成 Elasticsearch API 密钥", "xpack.enterpriseSearch.content.indices.configurationConnector.configuration.successToast.title": "已更新配置", - "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.clientExamplesLink": "连接器客户端示例", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.configurationFileLink": "配置文件", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.connectorDeployedText": "配置后,请在您的基础设施上部署连接器。", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.waitingForConnector.button.label": "立即重新检查", @@ -28644,7 +28642,6 @@ "xpack.observability.customThreshold.rule.alertFlyout.dataViewError.noTimestamp": "选定数据视图没有时间戳字段,请选择其他数据视图。", "xpack.observability.customThreshold.rule.alertFlyout.defineTextQueryPrompt": "定义查询筛选(可选)", "xpack.observability.customThreshold.rule.alertFlyout.error.equation.invalidCharacters": "方程字段仅支持以下字符:A-Z、+、-、/、*、(、)、?、!、&、:、|、>、<、=", - "xpack.observability.customThreshold.rule.alertFlyout.error.invalidFilterQuery": "筛选查询无效。", "xpack.observability.customThreshold.rule.alertFlyout.error.invalidSearchConfiguration": "需要数据视图。", "xpack.observability.customThreshold.rule.alertFlyout.error.metrics.aggTypeRequired": "“聚合”必填", "xpack.observability.customThreshold.rule.alertFlyout.error.metrics.fieldRequired": "“字段”必填", @@ -28981,8 +28978,6 @@ "xpack.observability.slo.sloEdit.calendarTimeWindow.monthly": "每月", "xpack.observability.slo.sloEdit.calendarTimeWindow.weekly": "每周", "xpack.observability.slo.sloEdit.cancelButton": "取消", - "xpack.observability.slo.sloEdit.createAlert.ruleName": "SLO 消耗速度告警规则", - "xpack.observability.slo.sloEdit.createAlert.title": "创建", "xpack.observability.slo.sloEdit.createSloButton": "创建 SLO", "xpack.observability.slo.sloEdit.customKql.indexSelection.label": "索引", "xpack.observability.slo.sloEdit.dataPreviewChart.errorMessage": "当前指标设置无效", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_mute_alert.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_mute_alert.test.tsx index 1f40d31fbf79b..d697b59028e7c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_mute_alert.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_mute_alert.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook } from '@testing-library/react-hooks/dom'; import * as api from '../../../../lib/rule_api/mute_alert'; import { waitFor } from '@testing-library/react'; import { useKibana } from '../../../../../common/lib/kibana'; @@ -31,14 +31,12 @@ describe('useMuteAlert', () => { it('calls the api when invoked with the correct parameters', async () => { const muteAlertInstanceSpy = jest.spyOn(api, 'muteAlertInstance'); - const { waitForNextUpdate, result } = renderHook(() => useMuteAlert(), { + const { result } = renderHook(() => useMuteAlert(), { wrapper: appMockRender.AppWrapper, }); result.current.mutate(params); - await waitForNextUpdate(); - await waitFor(() => { expect(muteAlertInstanceSpy).toHaveBeenCalledWith({ id: params.ruleId, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_unmute_alert.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_unmute_alert.test.tsx index 7e51abd949d60..7d47cd75a386f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_unmute_alert.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_unmute_alert.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook } from '@testing-library/react-hooks/dom'; import * as api from '../../../../lib/rule_api/unmute_alert'; import { waitFor } from '@testing-library/react'; import { useKibana } from '../../../../../common/lib/kibana'; @@ -18,8 +18,7 @@ jest.mock('../../../../../common/lib/kibana'); const params = { ruleId: '', alertInstanceId: '' }; -// FLAKY: https://github.com/elastic/kibana/issues/174900 -describe.skip('useUnmuteAlert', () => { +describe('useUnmuteAlert', () => { const addErrorMock = useKibana().services.notifications.toasts.addError as jest.Mock; let appMockRender: AppMockRenderer; @@ -32,14 +31,12 @@ describe.skip('useUnmuteAlert', () => { it('calls the api when invoked with the correct parameters', async () => { const muteAlertInstanceSpy = jest.spyOn(api, 'unmuteAlertInstance'); - const { waitForNextUpdate, result } = renderHook(() => useUnmuteAlert(), { + const { result } = renderHook(() => useUnmuteAlert(), { wrapper: appMockRender.AppWrapper, }); result.current.mutate(params); - await waitForNextUpdate(); - await waitFor(() => { expect(muteAlertInstanceSpy).toHaveBeenCalledWith({ id: params.ruleId, diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/group_by_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/group_by_fired.ts index 13ff525daa022..ca73e43114441 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/group_by_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/group_by_fired.ts @@ -262,7 +262,7 @@ export default function ({ getService }: FtrProviderContext) { `https://localhost:5601/app/observability/alerts?_a=(kuery:%27kibana.alert.uuid:%20%22${alertId}%22%27%2CrangeFrom:%27${rangeFrom}%27%2CrangeTo:now%2Cstatus:all)` ); expect(resp.hits.hits[0]._source?.reason).eql( - `Average system.cpu.total.norm.pct is 80%, above the threshold of 20%. (duration: 1 min, data view: ${DATA_VIEW}, group: host-0,container-0)` + `Average system.cpu.total.norm.pct is 80%, above or equal the threshold of 20%. (duration: 1 min, data view: ${DATA_VIEW}, group: host-0,container-0)` ); expect(resp.hits.hits[0]._source?.value).eql('80%'); expect(resp.hits.hits[0]._source?.host).eql( diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/p99_pct_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/p99_pct_fired.ts new file mode 100644 index 0000000000000..942d025dd11f3 --- /dev/null +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/p99_pct_fired.ts @@ -0,0 +1,259 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment'; +import { omit } from 'lodash'; +import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; +import { + Aggregators, + Comparator, +} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; +import expect from '@kbn/expect'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; +import { parseSearchParams } from '@kbn/share-plugin/common/url_service'; +import { createIndexConnector, createRule } from '../helpers/alerting_api_helper'; +import { + waitForAlertInIndex, + waitForDocumentInIndex, + waitForRuleStatus, +} from '../helpers/alerting_wait_for_helpers'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { ActionDocument, LogsExplorerLocatorParsedParams } from './typings'; +import { ISO_DATE_REGEX } from './constants'; + +// eslint-disable-next-line import/no-default-export +export default function ({ getService }: FtrProviderContext) { + const esClient = getService('es'); + const supertest = getService('supertest'); + const esDeleteAllIndices = getService('esDeleteAllIndices'); + const logger = getService('log'); + + describe('Custom Threshold rule - P99 - PCT - FIRED', () => { + const CUSTOM_THRESHOLD_RULE_ALERT_INDEX = '.alerts-observability.threshold.alerts-default'; + const ALERT_ACTION_INDEX = 'alert-action-threshold'; + const DATE_VIEW_TITLE = 'kbn-data-forge-fake_hosts.fake_hosts-*'; + const DATE_VIEW_NAME = 'ad-hoc-data-view-name'; + const DATA_VIEW_ID = 'data-view-id'; + const MOCKED_AD_HOC_DATA_VIEW = { + id: DATA_VIEW_ID, + title: DATE_VIEW_TITLE, + timeFieldName: '@timestamp', + sourceFilters: [], + fieldFormats: {}, + runtimeFieldMap: {}, + allowNoIndex: false, + name: DATE_VIEW_NAME, + allowHidden: false, + }; + let dataForgeConfig: PartialConfig; + let dataForgeIndices: string[]; + let actionId: string; + let ruleId: string; + let alertId: string; + let startedAt: string; + + before(async () => { + dataForgeConfig = { + schedule: [ + { + template: 'good', + start: 'now-15m', + end: 'now', + metrics: [{ name: 'system.cpu.user.pct', method: 'linear', start: 2.5, end: 2.5 }], + }, + ], + indexing: { + dataset: 'fake_hosts' as Dataset, + eventsPerCycle: 1, + interval: 10000, + alignEventsToInterval: true, + }, + }; + dataForgeIndices = await generate({ client: esClient, config: dataForgeConfig, logger }); + logger.info(JSON.stringify(dataForgeIndices.join(','))); + await waitForDocumentInIndex({ + esClient, + indexName: DATE_VIEW_TITLE, + docCountTarget: 270, + }); + }); + + after(async () => { + await supertest.delete(`/api/alerting/rule/${ruleId}`).set('kbn-xsrf', 'foo'); + await supertest.delete(`/api/actions/connector/${actionId}`).set('kbn-xsrf', 'foo'); + await esClient.deleteByQuery({ + index: CUSTOM_THRESHOLD_RULE_ALERT_INDEX, + query: { term: { 'kibana.alert.rule.uuid': ruleId } }, + }); + await esClient.deleteByQuery({ + index: '.kibana-event-log-*', + query: { term: { 'kibana.alert.rule.consumer': 'logs' } }, + }); + await esDeleteAllIndices([ALERT_ACTION_INDEX, ...dataForgeIndices]); + await cleanup({ client: esClient, config: dataForgeConfig, logger }); + }); + + describe('Rule creation', () => { + it('creates rule successfully', async () => { + actionId = await createIndexConnector({ + supertest, + name: 'Index Connector: Threshold API test', + indexName: ALERT_ACTION_INDEX, + }); + + const createdRule = await createRule({ + supertest, + tags: ['observability'], + consumer: 'logs', + name: 'Threshold rule', + ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, + params: { + criteria: [ + { + comparator: Comparator.GT, + threshold: [0.5], + timeSize: 5, + timeUnit: 'm', + metrics: [{ name: 'A', field: 'system.cpu.user.pct', aggType: Aggregators.P99 }], + }, + ], + alertOnNoData: true, + alertOnGroupDisappear: true, + searchConfiguration: { + query: { + query: '', + language: 'kuery', + }, + index: MOCKED_AD_HOC_DATA_VIEW, + }, + }, + actions: [ + { + group: FIRED_ACTIONS_ID, + id: actionId, + params: { + documents: [ + { + ruleType: '{{rule.type}}', + alertDetailsUrl: '{{context.alertDetailsUrl}}', + reason: '{{context.reason}}', + value: '{{context.value}}', + host: '{{context.host}}', + viewInAppUrl: '{{context.viewInAppUrl}}', + }, + ], + }, + frequency: { + notify_when: 'onActionGroupChange', + throttle: null, + summary: false, + }, + }, + ], + }); + ruleId = createdRule.id; + expect(ruleId).not.to.be(undefined); + }); + + it('should be active', async () => { + const executionStatus = await waitForRuleStatus({ + id: ruleId, + expectedStatus: 'active', + supertest, + }); + expect(executionStatus.status).to.be('active'); + }); + + it('should set correct information in the alert document', async () => { + const resp = await waitForAlertInIndex({ + esClient, + indexName: CUSTOM_THRESHOLD_RULE_ALERT_INDEX, + ruleId, + }); + alertId = (resp.hits.hits[0]._source as any)['kibana.alert.uuid']; + startedAt = (resp.hits.hits[0]._source as any)['kibana.alert.start']; + + expect(resp.hits.hits[0]._source).property( + 'kibana.alert.rule.category', + 'Custom threshold (Beta)' + ); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.consumer', 'logs'); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.name', 'Threshold rule'); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.producer', 'observability'); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.revision', 0); + expect(resp.hits.hits[0]._source).property( + 'kibana.alert.rule.rule_type_id', + 'observability.rules.custom_threshold' + ); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.uuid', ruleId); + expect(resp.hits.hits[0]._source).property('kibana.space_ids').contain('default'); + expect(resp.hits.hits[0]._source) + .property('kibana.alert.rule.tags') + .contain('observability'); + expect(resp.hits.hits[0]._source).property( + 'kibana.alert.action_group', + 'custom_threshold.fired' + ); + expect(resp.hits.hits[0]._source).property('tags').contain('observability'); + expect(resp.hits.hits[0]._source).property('kibana.alert.instance.id', '*'); + expect(resp.hits.hits[0]._source).property('kibana.alert.workflow_status', 'open'); + expect(resp.hits.hits[0]._source).property('event.kind', 'signal'); + expect(resp.hits.hits[0]._source).property('event.action', 'open'); + + expect(resp.hits.hits[0]._source) + .property('kibana.alert.rule.parameters') + .eql({ + criteria: [ + { + comparator: '>', + threshold: [0.5], + timeSize: 5, + timeUnit: 'm', + metrics: [{ name: 'A', field: 'system.cpu.user.pct', aggType: 'p99' }], + }, + ], + alertOnNoData: true, + alertOnGroupDisappear: true, + searchConfiguration: { + index: MOCKED_AD_HOC_DATA_VIEW, + query: { query: '', language: 'kuery' }, + }, + }); + }); + + it('should set correct action variables', async () => { + const rangeFrom = moment(startedAt).subtract('5', 'minute').toISOString(); + const resp = await waitForDocumentInIndex({ + esClient, + indexName: ALERT_ACTION_INDEX, + }); + + expect(resp.hits.hits[0]._source?.ruleType).eql('observability.rules.custom_threshold'); + expect(resp.hits.hits[0]._source?.alertDetailsUrl).eql( + `https://localhost:5601/app/observability/alerts?_a=(kuery:%27kibana.alert.uuid:%20%22${alertId}%22%27%2CrangeFrom:%27${rangeFrom}%27%2CrangeTo:now%2Cstatus:all)` + ); + expect(resp.hits.hits[0]._source?.reason).eql( + `99th percentile of system.cpu.user.pct is 250%, above the threshold of 50%. (duration: 5 mins, data view: ${DATE_VIEW_NAME})` + ); + expect(resp.hits.hits[0]._source?.value).eql('250%'); + + const parsedViewInAppUrl = parseSearchParams( + new URL(resp.hits.hits[0]._source?.viewInAppUrl || '').search + ); + + expect(resp.hits.hits[0]._source?.viewInAppUrl).contain('LOGS_EXPLORER_LOCATOR'); + expect(omit(parsedViewInAppUrl.params, 'timeRange.from')).eql({ + dataset: DATE_VIEW_TITLE, + timeRange: { to: 'now' }, + query: { query: '', language: 'kuery' }, + }); + expect(parsedViewInAppUrl.params.timeRange.from).match(ISO_DATE_REGEX); + }); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/rate_bytes_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/rate_bytes_fired.ts new file mode 100644 index 0000000000000..b76dd0c2581fc --- /dev/null +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/rate_bytes_fired.ts @@ -0,0 +1,273 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment'; +import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; +import { + Aggregators, + Comparator, +} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; +import expect from '@kbn/expect'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; +import { createIndexConnector, createRule } from '../helpers/alerting_api_helper'; +import { createDataView, deleteDataView } from '../helpers/data_view'; +import { + waitForAlertInIndex, + waitForDocumentInIndex, + waitForRuleStatus, +} from '../helpers/alerting_wait_for_helpers'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { ActionDocument } from './typings'; + +// eslint-disable-next-line import/no-default-export +export default function ({ getService }: FtrProviderContext) { + const esClient = getService('es'); + const supertest = getService('supertest'); + const esDeleteAllIndices = getService('esDeleteAllIndices'); + const logger = getService('log'); + + describe('Custom Threshold rule RATE - GROUP_BY - BYTES - FIRED', () => { + const CUSTOM_THRESHOLD_RULE_ALERT_INDEX = '.alerts-observability.threshold.alerts-default'; + const ALERT_ACTION_INDEX = 'alert-action-threshold'; + const DATE_VIEW = 'kbn-data-forge-fake_hosts.fake_hosts-*'; + const DATA_VIEW_ID = 'data-view-id'; + let dataForgeConfig: PartialConfig; + let dataForgeIndices: string[]; + let actionId: string; + let ruleId: string; + let alertId: string; + let startedAt: string; + + before(async () => { + dataForgeConfig = { + schedule: [ + { + template: 'good', + start: 'now-15m', + end: 'now', + metrics: [{ name: 'system.network.in.bytes', method: 'exp', start: 10, end: 100 }], + }, + ], + indexing: { + dataset: 'fake_hosts' as Dataset, + eventsPerCycle: 1, + interval: 10000, + alignEventsToInterval: true, + }, + }; + dataForgeIndices = await generate({ client: esClient, config: dataForgeConfig, logger }); + await waitForDocumentInIndex({ + esClient, + indexName: dataForgeIndices.join(','), + docCountTarget: 270, + }); + await createDataView({ + supertest, + name: DATE_VIEW, + id: DATA_VIEW_ID, + title: DATE_VIEW, + }); + }); + + after(async () => { + await supertest.delete(`/api/alerting/rule/${ruleId}`).set('kbn-xsrf', 'foo'); + await supertest.delete(`/api/actions/connector/${actionId}`).set('kbn-xsrf', 'foo'); + await esClient.deleteByQuery({ + index: CUSTOM_THRESHOLD_RULE_ALERT_INDEX, + query: { term: { 'kibana.alert.rule.uuid': ruleId } }, + }); + await esClient.deleteByQuery({ + index: '.kibana-event-log-*', + query: { term: { 'kibana.alert.rule.consumer': 'logs' } }, + }); + await deleteDataView({ + supertest, + id: DATA_VIEW_ID, + }); + await esDeleteAllIndices([ALERT_ACTION_INDEX, ...dataForgeIndices]); + await cleanup({ client: esClient, config: dataForgeConfig, logger }); + }); + + describe('Rule creation', () => { + it('creates rule successfully', async () => { + actionId = await createIndexConnector({ + supertest, + name: 'Index Connector: Threshold API test', + indexName: ALERT_ACTION_INDEX, + }); + + const createdRule = await createRule({ + supertest, + tags: ['observability'], + consumer: 'logs', + name: 'Threshold rule', + ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, + params: { + criteria: [ + { + comparator: Comparator.GT_OR_EQ, + threshold: [0.2], + timeSize: 1, + timeUnit: 'm', + metrics: [ + { name: 'A', field: 'system.network.in.bytes', aggType: Aggregators.RATE }, + ], + }, + ], + alertOnNoData: true, + alertOnGroupDisappear: true, + searchConfiguration: { + query: { + query: '', + language: 'kuery', + }, + index: DATA_VIEW_ID, + }, + groupBy: ['host.name', 'container.id'], + }, + actions: [ + { + group: FIRED_ACTIONS_ID, + id: actionId, + params: { + documents: [ + { + ruleType: '{{rule.type}}', + alertDetailsUrl: '{{context.alertDetailsUrl}}', + reason: '{{context.reason}}', + value: '{{context.value}}', + host: '{{context.host}}', + group: '{{context.group}}', + }, + ], + }, + frequency: { + notify_when: 'onActionGroupChange', + throttle: null, + summary: false, + }, + }, + ], + }); + ruleId = createdRule.id; + expect(ruleId).not.to.be(undefined); + }); + + it('should be active', async () => { + const executionStatus = await waitForRuleStatus({ + id: ruleId, + expectedStatus: 'active', + supertest, + }); + expect(executionStatus.status).to.be('active'); + }); + + it('should set correct information in the alert document', async () => { + const resp = await waitForAlertInIndex({ + esClient, + indexName: CUSTOM_THRESHOLD_RULE_ALERT_INDEX, + ruleId, + }); + alertId = (resp.hits.hits[0]._source as any)['kibana.alert.uuid']; + startedAt = (resp.hits.hits[0]._source as any)['kibana.alert.start']; + + expect(resp.hits.hits[0]._source).property( + 'kibana.alert.rule.category', + 'Custom threshold (Beta)' + ); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.consumer', 'logs'); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.name', 'Threshold rule'); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.producer', 'observability'); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.revision', 0); + expect(resp.hits.hits[0]._source).property( + 'kibana.alert.rule.rule_type_id', + 'observability.rules.custom_threshold' + ); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.uuid', ruleId); + expect(resp.hits.hits[0]._source).property('kibana.space_ids').contain('default'); + expect(resp.hits.hits[0]._source) + .property('kibana.alert.rule.tags') + .contain('observability'); + expect(resp.hits.hits[0]._source).property( + 'kibana.alert.action_group', + 'custom_threshold.fired' + ); + expect(resp.hits.hits[0]._source).property('tags').contain('observability'); + expect(resp.hits.hits[0]._source).property( + 'kibana.alert.instance.id', + 'host-0,container-0' + ); + expect(resp.hits.hits[0]._source).property('kibana.alert.workflow_status', 'open'); + expect(resp.hits.hits[0]._source).property('event.kind', 'signal'); + expect(resp.hits.hits[0]._source).property('event.action', 'open'); + + expect(resp.hits.hits[0]._source).property('host.name', 'host-0'); + expect(resp.hits.hits[0]._source) + .property('host.mac') + .eql(['00-00-5E-00-53-23', '00-00-5E-00-53-24']); + expect(resp.hits.hits[0]._source).property('container.id', 'container-0'); + expect(resp.hits.hits[0]._source).property('container.name', 'container-name'); + expect(resp.hits.hits[0]._source).not.property('container.cpu'); + + expect(resp.hits.hits[0]._source) + .property('kibana.alert.group') + .eql([ + { + field: 'host.name', + value: 'host-0', + }, + { + field: 'container.id', + value: 'container-0', + }, + ]); + + expect(resp.hits.hits[0]._source) + .property('kibana.alert.rule.parameters') + .eql({ + criteria: [ + { + comparator: '>=', + threshold: [0.2], + timeSize: 1, + timeUnit: 'm', + metrics: [{ name: 'A', field: 'system.network.in.bytes', aggType: 'rate' }], + }, + ], + alertOnNoData: true, + alertOnGroupDisappear: true, + searchConfiguration: { index: 'data-view-id', query: { query: '', language: 'kuery' } }, + groupBy: ['host.name', 'container.id'], + }); + }); + + it('should set correct action variables', async () => { + const rangeFrom = moment(startedAt).subtract('5', 'minute').toISOString(); + const resp = await waitForDocumentInIndex({ + esClient, + indexName: ALERT_ACTION_INDEX, + }); + + expect(resp.hits.hits[0]._source?.ruleType).eql('observability.rules.custom_threshold'); + expect(resp.hits.hits[0]._source?.alertDetailsUrl).eql( + `https://localhost:5601/app/observability/alerts?_a=(kuery:%27kibana.alert.uuid:%20%22${alertId}%22%27%2CrangeFrom:%27${rangeFrom}%27%2CrangeTo:now%2Cstatus:all)` + ); + expect(resp.hits.hits[0]._source?.reason).eql( + `Rate of system.network.in.bytes is 0.3 B/s, above or equal the threshold of 0.2 B/s. (duration: 1 min, data view: kbn-data-forge-fake_hosts.fake_hosts-*, group: host-0,container-0)` + ); + expect(resp.hits.hits[0]._source?.value).eql('0.3 B/s'); + expect(resp.hits.hits[0]._source?.host).eql( + '{"name":"host-0","mac":["00-00-5E-00-53-23","00-00-5E-00-53-24"]}' + ); + expect(resp.hits.hits[0]._source?.group).eql( + '{"field":"host.name","value":"host-0"},{"field":"container.id","value":"container-0"}' + ); + }); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/observability/index.ts b/x-pack/test/alerting_api_integration/observability/index.ts index 884c17d2abfd1..812123dd96b13 100644 --- a/x-pack/test/alerting_api_integration/observability/index.ts +++ b/x-pack/test/alerting_api_integration/observability/index.ts @@ -11,6 +11,8 @@ export default function ({ loadTestFile }: any) { describe('Rules Endpoints', () => { loadTestFile(require.resolve('./metric_threshold_rule')); loadTestFile(require.resolve('./custom_threshold_rule/avg_pct_fired')); + loadTestFile(require.resolve('./custom_threshold_rule/p99_pct_fired')); + loadTestFile(require.resolve('./custom_threshold_rule/rate_bytes_fired')); loadTestFile(require.resolve('./custom_threshold_rule/avg_pct_no_data')); loadTestFile(require.resolve('./custom_threshold_rule/avg_us_fired')); loadTestFile(require.resolve('./custom_threshold_rule/custom_eq_avg_bytes_fired')); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/bulk_untrack.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/bulk_untrack.ts index b2710626032fe..8a9225694047c 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/bulk_untrack.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/bulk_untrack.ts @@ -21,6 +21,15 @@ export default function bulkUntrackTests({ getService }: FtrProviderContext) { const retry = getService('retry'); const es = getService('es'); + const runSoon = async (id: string) => { + return retry.try(async () => { + await supertest + .post(`${getUrlPrefix('space1')}/internal/alerting/rule/${id}/_run_soon`) + .set('kbn-xsrf', 'foo') + .expect(204); + }); + }; + describe('bulk untrack', () => { const objectRemover = new ObjectRemover(supertest); @@ -128,5 +137,90 @@ export default function bulkUntrackTests({ getService }: FtrProviderContext) { }); }); } + + it('should create new alerts if run rules again after alerts are untracked', async () => { + const { body: createdRule } = await supertest + .post(`${getUrlPrefix('space1')}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.always-firing-alert-as-data', + schedule: { interval: '24h' }, + throttle: undefined, + notify_when: undefined, + params: { + index: ES_TEST_INDEX_NAME, + reference: 'test', + }, + }) + ) + .expect(200); + + objectRemover.add('space1', createdRule.id, 'rule', 'alerting'); + + await retry.try(async () => { + return await getEventLog({ + getService, + spaceId: 'space1', + type: 'alert', + id: createdRule.id, + provider: 'alerting', + actions: new Map([['active-instance', { equal: 2 }]]), + }); + }); + + const { + hits: { hits: activeAlerts }, + } = await es.search({ + index: alertAsDataIndex, + body: { query: { match_all: {} } }, + }); + + const ids = activeAlerts.map((activeAlert: any) => activeAlert._source[ALERT_UUID]); + + await supertest + .post(`${getUrlPrefix('space1')}/internal/alerting/alerts/_bulk_untrack`) + .set('kbn-xsrf', 'foo') + .send({ + indices: [alertAsDataIndex], + alert_uuids: ids, + }); + + await runSoon(createdRule.id); + + await retry.try(async () => { + return await getEventLog({ + getService, + spaceId: 'space1', + type: 'alert', + id: createdRule.id, + provider: 'alerting', + actions: new Map([['active-instance', { equal: 4 }]]), + }); + }); + + await retry.try(async () => { + const { + hits: { hits: alerts }, + } = await es.search({ + index: alertAsDataIndex, + body: { query: { match_all: {} } }, + }); + + const activeAlertsRemaining = []; + const untrackedAlertsRemaining = []; + + alerts.forEach((alert: any) => { + if (alert._source[ALERT_STATUS] === 'active') { + activeAlertsRemaining.push(alert); + } else { + untrackedAlertsRemaining.push(alert); + } + }); + + expect(activeAlertsRemaining.length).eql(2); + expect(untrackedAlertsRemaining.length).eql(2); + }); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts index 5228b1c76d3d9..da3752e098de2 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts @@ -7,6 +7,7 @@ import moment from 'moment'; import expect from '@kbn/expect'; +import { get } from 'lodash'; import { IValidatedEvent, nanosToMillis } from '@kbn/event-log-plugin/server'; import { RuleNotifyWhen } from '@kbn/alerting-plugin/common'; import { ES_TEST_INDEX_NAME, ESTestIndexTool } from '@kbn/alerting-api-integration-helpers'; @@ -1849,7 +1850,12 @@ export default function eventLogTests({ getService }: FtrProviderContext) { expect(hasActions).eql(false); }); - it('should generate expected events with a notificationDelay', async () => { + it('should generate expected events with a alertDelay', async () => { + const ACTIVE_PATH = 'kibana.alert.rule.execution.metrics.alert_counts.active'; + const NEW_PATH = 'kibana.alert.rule.execution.metrics.alert_counts.new'; + const RECOVERED_PATH = 'kibana.alert.rule.execution.metrics.alert_counts.recovered'; + const ACTION_PATH = 'kibana.alert.rule.execution.metrics.number_of_triggered_actions'; + const { body: createdAction } = await supertest .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') @@ -1863,7 +1869,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { // pattern of when the alert should fire const pattern = { - instance: [true, true, true, false, true], + instance: [true, true, true, true, false, true], }; const response = await supertest @@ -1874,6 +1880,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { rule_type_id: 'test.patternFiring', schedule: { interval: '1s' }, throttle: null, + notify_when: null, params: { pattern, }, @@ -1882,9 +1889,14 @@ export default function eventLogTests({ getService }: FtrProviderContext) { id: createdAction.id, group: 'default', params: {}, + frequency: { + summary: false, + throttle: null, + notify_when: RuleNotifyWhen.CHANGE, + }, }, ], - notification_delay: { + alert_delay: { active: 3, }, }) @@ -1904,101 +1916,48 @@ export default function eventLogTests({ getService }: FtrProviderContext) { provider: 'alerting', actions: new Map([ // make sure the counts of the # of events per type are as expected - ['execute-start', { gte: 5 }], - ['execute', { gte: 5 }], - ['new-instance', { equal: 2 }], - ['active-instance', { gte: 1 }], + ['execute-start', { equal: 6 }], + ['execute', { equal: 6 }], + ['new-instance', { equal: 1 }], + ['active-instance', { equal: 2 }], ['recovered-instance', { equal: 1 }], ]), }); }); - const actualTriggeredActions = events - .filter((event) => event?.event?.action === 'execute') - .reduce( - (acc, event) => - acc + - (event?.kibana?.alert?.rule?.execution?.metrics - ?.number_of_triggered_actions as number), - 0 - ); - expect(actualTriggeredActions).to.eql(1); - }); - - it('should generate expected events with a notificationDelay with AAD', async () => { - const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .send({ - name: 'MY action', - connector_type_id: 'test.noop', - config: {}, - secrets: {}, - }) - .expect(200); - - // pattern of when the alert should fire - const pattern = { - instance: [true, true, true, false, true], - }; - - const response = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - rule_type_id: 'test.patternFiringAad', - schedule: { interval: '1s' }, - throttle: null, - params: { - pattern, - }, - actions: [ - { - id: createdAction.id, - group: 'default', - params: {}, - }, - ], - notification_delay: { - active: 3, - }, - }) - ); - - expect(response.status).to.eql(200); - const alertId = response.body.id; - objectRemover.add(space.id, alertId, 'rule', 'alerting'); + const executeEvents = events.filter((event) => event?.event?.action === 'execute'); - // get the events we're expecting - const events = await retry.try(async () => { - return await getEventLog({ - getService, - spaceId: space.id, - type: 'alert', - id: alertId, - provider: 'alerting', - actions: new Map([ - // make sure the counts of the # of events per type are as expected - ['execute-start', { gte: 5 }], - ['execute', { gte: 5 }], - ['new-instance', { equal: 2 }], - ['active-instance', { gte: 1 }], - ['recovered-instance', { equal: 1 }], - ]), - }); + // first two executions do not create the active alert + executeEvents.slice(0, 1).forEach((event) => { + expect(get(event, ACTIVE_PATH)).to.be(0); + expect(get(event, NEW_PATH)).to.be(0); + expect(get(event, RECOVERED_PATH)).to.be(0); + expect(get(event, ACTION_PATH)).to.be(0); }); - const actualTriggeredActions = events - .filter((event) => event?.event?.action === 'execute') - .reduce( - (acc, event) => - acc + - (event?.kibana?.alert?.rule?.execution?.metrics - ?.number_of_triggered_actions as number), - 0 - ); - expect(actualTriggeredActions).to.eql(1); + // third executions creates the delayed active alert and triggers actions + expect(get(executeEvents[2], ACTIVE_PATH)).to.be(1); + expect(get(executeEvents[2], NEW_PATH)).to.be(1); + expect(get(executeEvents[2], RECOVERED_PATH)).to.be(0); + expect(get(executeEvents[2], ACTION_PATH)).to.be(1); + + // fourth execution + expect(get(executeEvents[3], ACTIVE_PATH)).to.be(1); + expect(get(executeEvents[3], NEW_PATH)).to.be(0); + expect(get(executeEvents[3], RECOVERED_PATH)).to.be(0); + expect(get(executeEvents[3], ACTION_PATH)).to.be(0); + + // fifth recovered execution + expect(get(executeEvents[4], ACTIVE_PATH)).to.be(0); + expect(get(executeEvents[4], NEW_PATH)).to.be(0); + expect(get(executeEvents[4], RECOVERED_PATH)).to.be(1); + expect(get(executeEvents[4], ACTION_PATH)).to.be(0); + + // sixth execution does not create the active alert + expect(get(executeEvents[5], ACTIVE_PATH)).to.be(0); + expect(get(executeEvents[5], NEW_PATH)).to.be(0); + expect(get(executeEvents[5], RECOVERED_PATH)).to.be(0); + expect(get(executeEvents[5], ACTION_PATH)).to.be(0); }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/notification_delay.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alert_delay.ts similarity index 82% rename from x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/notification_delay.ts rename to x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alert_delay.ts index 2b632686d5793..7062c1c65fd9c 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/notification_delay.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alert_delay.ts @@ -12,7 +12,7 @@ import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common import { Spaces } from '../../../scenarios'; // eslint-disable-next-line import/no-default-export -export default function createNotificationDelayTests({ getService }: FtrProviderContext) { +export default function createAlertDelayTests({ getService }: FtrProviderContext) { const es = getService('es'); const supertestWithoutAuth = getService('supertestWithoutAuth'); const retry = getService('retry'); @@ -22,7 +22,7 @@ export default function createNotificationDelayTests({ getService }: FtrProvider const ACTIVE_PATH = 'alertInstances.instance.meta.activeCount'; const RECOVERED_PATH = 'alertRecoveredInstances.instance.meta.activeCount'; - describe('Notification Delay', () => { + describe('Alert Delay', () => { let actionId: string; const objectRemover = new ObjectRemover(supertestWithoutAuth); @@ -37,20 +37,7 @@ export default function createNotificationDelayTests({ getService }: FtrProvider afterEach(() => objectRemover.removeAll()); - it('should clear the activeCount if the notificationDelay is not configured for the rule', async () => { - const start = new Date().toISOString(); - const pattern = { - instance: [true], - }; - - const ruleId = await createRule(actionId, pattern); - objectRemover.add(space.id, ruleId, 'rule', 'alerting'); - - const state = await getAlertState(start, ruleId, 0); - expect(get(state, ACTIVE_PATH)).to.eql(0); - }); - - it('should update the activeCount when alert is active and clear when recovered if the notificationDelay is configured for the rule', async () => { + it('should update the activeCount when alert is active and clear when recovered', async () => { let start = new Date().toISOString(); const pattern = { instance: [true, true, true, false, true], @@ -79,7 +66,7 @@ export default function createNotificationDelayTests({ getService }: FtrProvider expect(get(state, ACTIVE_PATH)).to.eql(1); }); - it('should reset the activeCount when count of consecutive active alerts exceeds the notificationDelay count', async () => { + it('should continue incrementing the activeCount when count of consecutive active alerts exceeds the alertDelay count', async () => { let start = new Date().toISOString(); const pattern = { instance: [true, true, true, true, true], @@ -96,16 +83,16 @@ export default function createNotificationDelayTests({ getService }: FtrProvider expect(get(state, ACTIVE_PATH)).to.eql(2); start = new Date().toISOString(); - state = await getAlertState(start, ruleId, 0, true); - expect(get(state, ACTIVE_PATH)).to.eql(0); + state = await getAlertState(start, ruleId, 3, true); + expect(get(state, ACTIVE_PATH)).to.eql(3); start = new Date().toISOString(); - state = await getAlertState(start, ruleId, 1, true); - expect(get(state, ACTIVE_PATH)).to.eql(1); + state = await getAlertState(start, ruleId, 4, true); + expect(get(state, ACTIVE_PATH)).to.eql(4); start = new Date().toISOString(); - state = await getAlertState(start, ruleId, 2, true); - expect(get(state, ACTIVE_PATH)).to.eql(2); + state = await getAlertState(start, ruleId, 5, true); + expect(get(state, ACTIVE_PATH)).to.eql(5); }); }); @@ -187,7 +174,7 @@ export default function createNotificationDelayTests({ getService }: FtrProvider params: {}, }, ], - ...(activeCount ? { notification_delay: { active: activeCount } } : {}), + ...(activeCount ? { alert_delay: { active: activeCount } } : {}), }) ) .expect(200); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data_alert_delay.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data_alert_delay.ts new file mode 100644 index 0000000000000..f7e2876e9775b --- /dev/null +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data_alert_delay.ts @@ -0,0 +1,665 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { get } from 'lodash'; +import { SearchHit } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { IValidatedEvent } from '@kbn/event-log-plugin/server'; +import type { Alert } from '@kbn/alerts-as-data-utils'; +import { + ALERT_ACTION_GROUP, + ALERT_DURATION, + ALERT_END, + ALERT_RULE_CATEGORY, + ALERT_RULE_CONSUMER, + ALERT_RULE_EXECUTION_UUID, + ALERT_RULE_NAME, + ALERT_RULE_PARAMETERS, + ALERT_RULE_PRODUCER, + ALERT_RULE_TAGS, + ALERT_RULE_TYPE_ID, + ALERT_RULE_UUID, + ALERT_START, + ALERT_STATUS, + ALERT_TIME_RANGE, + ALERT_UUID, + ALERT_WORKFLOW_STATUS, + EVENT_ACTION, + EVENT_KIND, + SPACE_IDS, +} from '@kbn/rule-data-utils'; +import { RuleNotifyWhen } from '@kbn/alerting-plugin/common'; +import { ES_TEST_INDEX_NAME, ESTestIndexTool } from '@kbn/alerting-api-integration-helpers'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { Spaces } from '../../../../scenarios'; +import { + getEventLog, + getTestRuleData, + getUrlPrefix, + ObjectRemover, + TaskManagerDoc, +} from '../../../../../common/lib'; + +// eslint-disable-next-line import/no-default-export +export default function createAlertsAsDataAlertDelayInstallResourcesTest({ + getService, +}: FtrProviderContext) { + const ACTIVE_PATH = 'kibana.alert.rule.execution.metrics.alert_counts.active'; + const NEW_PATH = 'kibana.alert.rule.execution.metrics.alert_counts.new'; + const RECOVERED_PATH = 'kibana.alert.rule.execution.metrics.alert_counts.recovered'; + const ACTION_PATH = 'kibana.alert.rule.execution.metrics.number_of_triggered_actions'; + const UUID_PATH = 'kibana.alert.rule.execution.uuid'; + + const es = getService('es'); + const retry = getService('retry'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const objectRemover = new ObjectRemover(supertestWithoutAuth); + const esTestIndexTool = new ESTestIndexTool(es, retry); + + type PatternFiringAlert = Alert & { patternIndex: number; instancePattern: boolean[] }; + // type AlwaysFiringAlert = Alert & { patternIndex: number; instancePattern: boolean[] }; + + const alertsAsDataIndex = '.alerts-test.patternfiring.alerts-default'; + const alwaysFiringAlertsAsDataIndex = + '.internal.alerts-observability.test.alerts.alerts-default-000001'; + const timestampPattern = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/; + + describe('alerts as data', () => { + before(async () => { + await esTestIndexTool.destroy(); + await esTestIndexTool.setup(); + await es.deleteByQuery({ + index: [alertsAsDataIndex, alwaysFiringAlertsAsDataIndex], + query: { match_all: {} }, + conflicts: 'proceed', + }); + }); + afterEach(() => objectRemover.removeAll()); + after(async () => { + await objectRemover.removeAll(); + await esTestIndexTool.destroy(); + await es.deleteByQuery({ + index: [alertsAsDataIndex, alwaysFiringAlertsAsDataIndex], + query: { match_all: {} }, + conflicts: 'proceed', + }); + }); + + it('should generate expected events with a alertDelay with AAD', async () => { + const { body: createdAction } = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + // pattern of when the alert should fire + const pattern = { + instance: [true, true, true, true, false, true], + }; + + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.patternFiringAad', + schedule: { interval: '1d' }, + throttle: null, + notify_when: null, + params: { + pattern, + }, + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + frequency: { + summary: false, + throttle: null, + notify_when: RuleNotifyWhen.CHANGE, + }, + }, + ], + alert_delay: { + active: 3, + }, + }) + ); + + expect(response.status).to.eql(200); + const ruleId = response.body.id; + objectRemover.add(Spaces.space1.id, ruleId, 'rule', 'alerting'); + + // -------------------------- + // RUN 1 - 0 new alerts + // -------------------------- + let events: IValidatedEvent[] = await waitForEventLogDocs( + ruleId, + new Map([['execute', { equal: 1 }]]) + ); + let executeEvent = events[0]; + expect(get(executeEvent, ACTIVE_PATH)).to.be(0); + expect(get(executeEvent, NEW_PATH)).to.be(0); + expect(get(executeEvent, RECOVERED_PATH)).to.be(0); + expect(get(executeEvent, ACTION_PATH)).to.be(0); + + // Query for alerts + const alertDocsRun1 = await queryForAlertDocs(); + + // Get alert state from task document + let state: any = await getTaskState(ruleId); + expect(state.alertInstances.instance.meta.activeCount).to.equal(1); + expect(state.alertInstances.instance.state.patternIndex).to.equal(0); + + // After the first run, we should have 0 alert docs for the 0 active alerts + expect(alertDocsRun1.length).to.equal(0); + + // -------------------------- + // RUN 2 - 0 new alerts + // -------------------------- + let runSoon = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${ruleId}/_run_soon`) + .set('kbn-xsrf', 'foo'); + expect(runSoon.status).to.eql(204); + + events = await waitForEventLogDocs(ruleId, new Map([['execute', { equal: 2 }]])); + executeEvent = events[1]; + expect(get(executeEvent, ACTIVE_PATH)).to.be(0); + expect(get(executeEvent, NEW_PATH)).to.be(0); + expect(get(executeEvent, RECOVERED_PATH)).to.be(0); + expect(get(executeEvent, ACTION_PATH)).to.be(0); + + // Query for alerts + const alertDocsRun2 = await queryForAlertDocs(); + + // Get alert state from task document + state = await getTaskState(ruleId); + expect(state.alertInstances.instance.meta.activeCount).to.equal(2); + expect(state.alertInstances.instance.state.patternIndex).to.equal(1); + + // After the second run, we should have 0 alert docs for the 0 active alerts + expect(alertDocsRun2.length).to.equal(0); + + // -------------------------- + // RUN 3 - 1 new alert + // -------------------------- + runSoon = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${ruleId}/_run_soon`) + .set('kbn-xsrf', 'foo'); + expect(runSoon.status).to.eql(204); + + events = await waitForEventLogDocs(ruleId, new Map([['execute', { equal: 3 }]])); + executeEvent = events[2]; + let executionUuid = get(executeEvent, UUID_PATH); + expect(get(executeEvent, ACTIVE_PATH)).to.be(1); + expect(get(executeEvent, NEW_PATH)).to.be(1); + expect(get(executeEvent, RECOVERED_PATH)).to.be(0); + expect(get(executeEvent, ACTION_PATH)).to.be(1); + + // Query for alerts + const alertDocsRun3 = await queryForAlertDocs(); + + // Get alert state from task document + state = await getTaskState(ruleId); + expect(state.alertInstances.instance.meta.activeCount).to.equal(3); + expect(state.alertInstances.instance.state.patternIndex).to.equal(2); + + // After the third run, we should have 1 alert docs for the 1 active alert + expect(alertDocsRun3.length).to.equal(1); + + testExpectRuleData(alertDocsRun3, ruleId, { pattern }, executionUuid!); + let source: PatternFiringAlert = alertDocsRun3[0]._source!; + + // Each doc should have active status and default action group id + expect(source[ALERT_ACTION_GROUP]).to.equal('default'); + // patternIndex should be 2 for the third run + expect(source.patternIndex).to.equal(2); + // alert UUID should equal doc id + expect(source[ALERT_UUID]).to.equal(alertDocsRun3[0]._id); + // duration should be 0 since this is a new alert + expect(source[ALERT_DURATION]).to.equal(0); + // start should be defined + expect(source[ALERT_START]).to.match(timestampPattern); + // time_range.gte should be same as start + expect(source[ALERT_TIME_RANGE]?.gte).to.equal(source[ALERT_START]); + // timestamp should be defined + expect(source['@timestamp']).to.match(timestampPattern); + // status should be active + expect(source[ALERT_STATUS]).to.equal('active'); + // workflow status should be 'open' + expect(source[ALERT_WORKFLOW_STATUS]).to.equal('open'); + // event.action should be 'open' + expect(source[EVENT_ACTION]).to.equal('open'); + // event.kind should be 'signal' + expect(source[EVENT_KIND]).to.equal('signal'); + // tags should equal rule tags because rule type doesn't set any tags + expect(source.tags).to.eql(['foo']); + + // -------------------------- + // RUN 4 - 1 active alert + // -------------------------- + runSoon = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${ruleId}/_run_soon`) + .set('kbn-xsrf', 'foo'); + expect(runSoon.status).to.eql(204); + + events = await waitForEventLogDocs(ruleId, new Map([['execute', { equal: 4 }]])); + executeEvent = events[3]; + executionUuid = get(executeEvent, UUID_PATH); + expect(get(executeEvent, ACTIVE_PATH)).to.be(1); + expect(get(executeEvent, NEW_PATH)).to.be(0); + expect(get(executeEvent, RECOVERED_PATH)).to.be(0); + expect(get(executeEvent, ACTION_PATH)).to.be(0); + + // Query for alerts + const alertDocsRun4 = await queryForAlertDocs(); + + // Get alert state from task document + state = await getTaskState(ruleId); + expect(state.alertInstances.instance.meta.activeCount).to.equal(4); + expect(state.alertInstances.instance.state.patternIndex).to.equal(3); + + // After the fourth run, we should have 1 alert docs for the 1 active alert + expect(alertDocsRun4.length).to.equal(1); + + testExpectRuleData(alertDocsRun4, ruleId, { pattern }, executionUuid!); + source = alertDocsRun4[0]._source!; + const run3Source = alertDocsRun3[0]._source!; + + expect(source[ALERT_UUID]).to.equal(run3Source[ALERT_UUID]); + // patternIndex should be 3 for the fourth run + expect(source.patternIndex).to.equal(3); + expect(source[ALERT_ACTION_GROUP]).to.equal('default'); + // start time should be defined and the same as prior run + expect(source[ALERT_START]).to.match(timestampPattern); + expect(source[ALERT_START]).to.equal(run3Source[ALERT_START]); + // timestamp should be defined and not the same as prior run + expect(source['@timestamp']).to.match(timestampPattern); + expect(source['@timestamp']).not.to.equal(run3Source['@timestamp']); + // status should still be active + expect(source[ALERT_STATUS]).to.equal('active'); + // event.action set to active + expect(source[EVENT_ACTION]).to.eql('active'); + expect(source.tags).to.eql(['foo']); + // these values should be the same as previous run + expect(source[EVENT_KIND]).to.eql(run3Source[EVENT_KIND]); + expect(source[ALERT_WORKFLOW_STATUS]).to.eql(run3Source[ALERT_WORKFLOW_STATUS]); + expect(source[ALERT_TIME_RANGE]?.gte).to.equal(run3Source[ALERT_TIME_RANGE]?.gte); + + // -------------------------- + // RUN 5 - 1 recovered alert + // -------------------------- + runSoon = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${ruleId}/_run_soon`) + .set('kbn-xsrf', 'foo'); + expect(runSoon.status).to.eql(204); + + events = await waitForEventLogDocs(ruleId, new Map([['execute', { equal: 5 }]])); + executeEvent = events[4]; + executionUuid = get(executeEvent, UUID_PATH); + expect(get(executeEvent, ACTIVE_PATH)).to.be(0); + expect(get(executeEvent, NEW_PATH)).to.be(0); + expect(get(executeEvent, RECOVERED_PATH)).to.be(1); + expect(get(executeEvent, ACTION_PATH)).to.be(0); + + // Query for alerts + const alertDocsRun5 = await queryForAlertDocs(); + + // Get alert state from task document + state = await getTaskState(ruleId); + expect(state.alertRecoveredInstances.instance.meta.activeCount).to.equal(0); + + // After the fourth run, we should have 1 alert docs for the 1 recovered alert + expect(alertDocsRun5.length).to.equal(1); + + testExpectRuleData(alertDocsRun5, ruleId, { pattern }, executionUuid!); + source = alertDocsRun5[0]._source!; + + // action group should be set to recovered + expect(source[ALERT_ACTION_GROUP]).to.be('recovered'); + // rule type AAD payload should be set to recovery values + expect(source.instancePattern).to.eql([]); + expect(source.patternIndex).to.eql(-1); + // uuid is the same + expect(source[ALERT_UUID]).to.equal(run3Source[ALERT_UUID]); + // start time should be defined and the same as before + expect(source[ALERT_START]).to.match(timestampPattern); + expect(source[ALERT_START]).to.equal(run3Source[ALERT_START]); + // timestamp should be defined and not the same as prior run + expect(source['@timestamp']).to.match(timestampPattern); + expect(source['@timestamp']).not.to.equal(run3Source['@timestamp']); + // end time should be defined + expect(source[ALERT_END]).to.match(timestampPattern); + // status should be set to recovered + expect(source[ALERT_STATUS]).to.equal('recovered'); + // event.action set to close + expect(source[EVENT_ACTION]).to.eql('close'); + expect(source.tags).to.eql(['foo']); + // these values should be the same as previous run + expect(source[EVENT_KIND]).to.eql(run3Source[EVENT_KIND]); + expect(source[ALERT_WORKFLOW_STATUS]).to.eql(run3Source[ALERT_WORKFLOW_STATUS]); + expect(source[ALERT_TIME_RANGE]?.gte).to.equal(run3Source[ALERT_TIME_RANGE]?.gte); + // time_range.lte should be set to end time + expect(source[ALERT_TIME_RANGE]?.lte).to.equal(source[ALERT_END]); + + // -------------------------- + // RUN 6 - 0 new alerts + // -------------------------- + runSoon = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${ruleId}/_run_soon`) + .set('kbn-xsrf', 'foo'); + expect(runSoon.status).to.eql(204); + + events = await waitForEventLogDocs(ruleId, new Map([['execute', { equal: 6 }]])); + executeEvent = events[5]; + expect(get(executeEvent, ACTIVE_PATH)).to.be(0); + expect(get(executeEvent, NEW_PATH)).to.be(0); + expect(get(executeEvent, RECOVERED_PATH)).to.be(0); + expect(get(executeEvent, ACTION_PATH)).to.be(0); + + // Query for alerts + const alertDocsRun6 = await queryForAlertDocs(); + + // Get alert state from task document + state = await getTaskState(ruleId); + expect(state.alertInstances.instance.meta.activeCount).to.equal(1); + expect(state.alertInstances.instance.state.patternIndex).to.equal(5); + + // After the sixth run, we should have 1 alert docs for the previously recovered alert + expect(alertDocsRun6.length).to.equal(1); + }); + + it('should generate expected events with a alertDelay with AAD (rule registry)', async () => { + const params = { + index: ES_TEST_INDEX_NAME, + reference: 'test', + }; + const { body: createdAction } = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.always-firing-alert-as-data', + schedule: { interval: '1d' }, + throttle: null, + notify_when: null, + params, + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + frequency: { + summary: false, + throttle: null, + notify_when: RuleNotifyWhen.CHANGE, + }, + }, + ], + alert_delay: { + active: 3, + }, + }) + ); + + expect(response.status).to.eql(200); + const ruleId = response.body.id; + objectRemover.add(Spaces.space1.id, ruleId, 'rule', 'alerting'); + + // -------------------------- + // RUN 1 - 0 new alerts + // -------------------------- + let events: IValidatedEvent[] = await waitForEventLogDocs( + ruleId, + new Map([['execute', { equal: 1 }]]) + ); + let executeEvent = events[0]; + expect(get(executeEvent, ACTIVE_PATH)).to.be(0); + expect(get(executeEvent, NEW_PATH)).to.be(0); + expect(get(executeEvent, RECOVERED_PATH)).to.be(0); + expect(get(executeEvent, ACTION_PATH)).to.be(0); + + // Query for alerts + const alertDocsRun1 = await queryForAlertDocs(alwaysFiringAlertsAsDataIndex); + + // Get alert state from task document + let state: any = await getTaskState(ruleId); + expect(state.alertInstances['1'].meta.activeCount).to.equal(1); + expect(state.alertTypeState.trackedAlerts['1'].activeCount).to.equal(1); + + // After the first run, we should have 0 alert docs for the 0 active alerts + expect(alertDocsRun1.length).to.equal(0); + + // -------------------------- + // RUN 2 - 0 new alerts + // -------------------------- + let runSoon = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${ruleId}/_run_soon`) + .set('kbn-xsrf', 'foo'); + expect(runSoon.status).to.eql(204); + + events = await waitForEventLogDocs(ruleId, new Map([['execute', { equal: 2 }]])); + executeEvent = events[1]; + expect(get(executeEvent, ACTIVE_PATH)).to.be(0); + expect(get(executeEvent, NEW_PATH)).to.be(0); + expect(get(executeEvent, RECOVERED_PATH)).to.be(0); + expect(get(executeEvent, ACTION_PATH)).to.be(0); + + // Query for alerts + const alertDocsRun2 = await queryForAlertDocs(alwaysFiringAlertsAsDataIndex); + + // Get alert state from task document + state = await getTaskState(ruleId); + expect(state.alertInstances['1'].meta.activeCount).to.equal(2); + expect(state.alertTypeState.trackedAlerts['1'].activeCount).to.equal(2); + + // After the second run, we should have 0 alert docs for the 0 active alerts + expect(alertDocsRun2.length).to.equal(0); + + // -------------------------- + // RUN 3 - 1 new alert + // -------------------------- + runSoon = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${ruleId}/_run_soon`) + .set('kbn-xsrf', 'foo'); + expect(runSoon.status).to.eql(204); + + events = await waitForEventLogDocs(ruleId, new Map([['execute', { equal: 3 }]])); + executeEvent = events[2]; + let executionUuid = get(executeEvent, UUID_PATH); + // Note: the rule creates 2 alerts but we will only look at one + expect(get(executeEvent, ACTIVE_PATH)).to.be(2); + expect(get(executeEvent, NEW_PATH)).to.be(2); + expect(get(executeEvent, RECOVERED_PATH)).to.be(0); + expect(get(executeEvent, ACTION_PATH)).to.be(2); + + // Query for alerts + const alertDocsRun3 = await queryForAlertDocs(alwaysFiringAlertsAsDataIndex); + + // Get alert state from task document + state = await getTaskState(ruleId); + expect(state.alertInstances['1'].meta.activeCount).to.equal(3); + expect(state.alertTypeState.trackedAlerts['1'].activeCount).to.equal(3); + + // After the third run, we should have 2 alert docs for the 2 active alerts but we will only look at one + expect(alertDocsRun3.length).to.equal(2); + + let source: Alert = alertDocsRun3[0]._source!; + + // Each doc should have a copy of the rule data + expect(source[ALERT_RULE_CATEGORY]).to.equal('Test: Always Firing Alert As Data'); + expect(source[ALERT_RULE_CONSUMER]).to.equal('alertsFixture'); + expect(source[ALERT_RULE_NAME]).to.equal('abc'); + expect(source[ALERT_RULE_PRODUCER]).to.equal('alertsFixture'); + expect(source[ALERT_RULE_TAGS]).to.eql(['foo']); + expect(source[ALERT_RULE_TYPE_ID]).to.equal('test.always-firing-alert-as-data'); + expect(source[ALERT_RULE_UUID]).to.equal(ruleId); + expect(source[ALERT_RULE_PARAMETERS]).to.eql(params); + expect(source[SPACE_IDS]).to.eql(['space1']); + expect(source[ALERT_RULE_EXECUTION_UUID]).to.equal(executionUuid); + // alert UUID should equal doc id + expect(source[ALERT_UUID]).to.equal(alertDocsRun3[0]._id); + // duration should be 0 since this is a new alert + expect(source[ALERT_DURATION]).to.equal(0); + // start should be defined + expect(source[ALERT_START]).to.match(timestampPattern); + // time_range.gte should be same as start + expect(source[ALERT_TIME_RANGE]?.gte).to.equal(source[ALERT_START]); + // timestamp should be defined + expect(source['@timestamp']).to.match(timestampPattern); + // status should be active + expect(source[ALERT_STATUS]).to.equal('active'); + // workflow status should be 'open' + expect(source[ALERT_WORKFLOW_STATUS]).to.equal('open'); + // event.action should be 'open' + expect(source[EVENT_ACTION]).to.equal('open'); + // event.kind should be 'signal' + expect(source[EVENT_KIND]).to.equal('signal'); + // tags should equal rule tags because rule type doesn't set any tags + expect(source.tags).to.eql(['foo']); + + // -------------------------- + // RUN 4 - 1 active alert + // -------------------------- + runSoon = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${ruleId}/_run_soon`) + .set('kbn-xsrf', 'foo'); + expect(runSoon.status).to.eql(204); + + events = await waitForEventLogDocs(ruleId, new Map([['execute', { equal: 4 }]])); + executeEvent = events[3]; + executionUuid = get(executeEvent, UUID_PATH); + // Note: the rule creates 2 alerts but we will only look at one + expect(get(executeEvent, ACTIVE_PATH)).to.be(2); + expect(get(executeEvent, NEW_PATH)).to.be(0); + expect(get(executeEvent, RECOVERED_PATH)).to.be(0); + expect(get(executeEvent, ACTION_PATH)).to.be(0); + + // Query for alerts + const alertDocsRun4 = await queryForAlertDocs(alwaysFiringAlertsAsDataIndex); + + // Get alert state from task document + state = await getTaskState(ruleId); + expect(state.alertInstances['1'].meta.activeCount).to.equal(4); + expect(state.alertTypeState.trackedAlerts['1'].activeCount).to.equal(4); + + // After the fourth run, we should have 2 alert docs for the 2 active alerts but we will only look at one + expect(alertDocsRun4.length).to.equal(2); + + source = alertDocsRun4[0]._source!; + const run3Source = alertDocsRun3[0]._source!; + + // Each doc should have a copy of the rule data + expect(source[ALERT_RULE_CATEGORY]).to.equal('Test: Always Firing Alert As Data'); + expect(source[ALERT_RULE_CONSUMER]).to.equal('alertsFixture'); + expect(source[ALERT_RULE_NAME]).to.equal('abc'); + expect(source[ALERT_RULE_PRODUCER]).to.equal('alertsFixture'); + expect(source[ALERT_RULE_TAGS]).to.eql(['foo']); + expect(source[ALERT_RULE_TYPE_ID]).to.equal('test.always-firing-alert-as-data'); + expect(source[ALERT_RULE_UUID]).to.equal(ruleId); + expect(source[ALERT_RULE_PARAMETERS]).to.eql(params); + expect(source[SPACE_IDS]).to.eql(['space1']); + expect(source[ALERT_RULE_EXECUTION_UUID]).to.equal(executionUuid); + expect(source[ALERT_UUID]).to.equal(run3Source[ALERT_UUID]); + // start time should be defined and the same as prior run + expect(source[ALERT_START]).to.match(timestampPattern); + expect(source[ALERT_START]).to.equal(run3Source[ALERT_START]); + // timestamp should be defined and not the same as prior run + expect(source['@timestamp']).to.match(timestampPattern); + expect(source['@timestamp']).not.to.equal(run3Source['@timestamp']); + // status should still be active + expect(source[ALERT_STATUS]).to.equal('active'); + // event.action set to active + expect(source[EVENT_ACTION]).to.eql('active'); + expect(source.tags).to.eql(['foo']); + // these values should be the same as previous run + expect(source[EVENT_KIND]).to.eql(run3Source[EVENT_KIND]); + expect(source[ALERT_WORKFLOW_STATUS]).to.eql(run3Source[ALERT_WORKFLOW_STATUS]); + expect(source[ALERT_TIME_RANGE]?.gte).to.equal(run3Source[ALERT_TIME_RANGE]?.gte); + }); + }); + + function testExpectRuleData( + alertDocs: Array>, + ruleId: string, + ruleParameters: unknown, + executionUuid?: string + ) { + for (let i = 0; i < alertDocs.length; ++i) { + const source: PatternFiringAlert = alertDocs[i]._source!; + + // Each doc should have a copy of the rule data + expect(source[ALERT_RULE_CATEGORY]).to.equal( + 'Test: Firing on a Pattern and writing Alerts as Data' + ); + expect(source[ALERT_RULE_CONSUMER]).to.equal('alertsFixture'); + expect(source[ALERT_RULE_NAME]).to.equal('abc'); + expect(source[ALERT_RULE_PRODUCER]).to.equal('alertsFixture'); + expect(source[ALERT_RULE_TAGS]).to.eql(['foo']); + expect(source[ALERT_RULE_TYPE_ID]).to.equal('test.patternFiringAad'); + expect(source[ALERT_RULE_UUID]).to.equal(ruleId); + expect(source[ALERT_RULE_PARAMETERS]).to.eql(ruleParameters); + expect(source[SPACE_IDS]).to.eql(['space1']); + + if (executionUuid) { + expect(source[ALERT_RULE_EXECUTION_UUID]).to.equal(executionUuid); + } + } + } + + async function queryForAlertDocs( + index: string = alertsAsDataIndex + ): Promise>> { + const searchResult = await es.search({ + index, + body: { query: { match_all: {} } }, + }); + return searchResult.hits.hits as Array>; + } + + async function getTaskState(ruleId: string) { + const task = await es.get({ + id: `task:${ruleId}`, + index: '.kibana_task_manager', + }); + + return JSON.parse(task._source!.task.state); + } + + async function waitForEventLogDocs( + id: string, + actions: Map + ) { + return await retry.try(async () => { + return await getEventLog({ + getService, + spaceId: Spaces.space1.id, + type: 'alert', + id, + provider: 'alerting', + actions, + }); + }); + } +} diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/index.ts index 20342e053016d..e1a29d1c4bf3e 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/index.ts @@ -14,5 +14,6 @@ export default function alertsAsDataTests({ loadTestFile }: FtrProviderContext) loadTestFile(require.resolve('./alerts_as_data')); loadTestFile(require.resolve('./alerts_as_data_flapping')); loadTestFile(require.resolve('./alerts_as_data_conflicts')); + loadTestFile(require.resolve('./alerts_as_data_alert_delay')); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/index.ts index b73477cf3df30..15084a47f4d86 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/index.ts @@ -28,7 +28,7 @@ export default function alertingTests({ loadTestFile, getService }: FtrProviderC loadTestFile(require.resolve('./run_soon')); loadTestFile(require.resolve('./flapping_history')); loadTestFile(require.resolve('./check_registered_rule_types')); - loadTestFile(require.resolve('./notification_delay')); + loadTestFile(require.resolve('./alert_delay')); loadTestFile(require.resolve('./generate_alert_schemas')); // Do not place test files here, due to https://github.com/elastic/kibana/issues/123059 diff --git a/x-pack/test/api_integration/apis/maps/bsearch.ts b/x-pack/test/api_integration/apis/maps/bsearch.ts new file mode 100644 index 0000000000000..1813bcd0675c5 --- /dev/null +++ b/x-pack/test/api_integration/apis/maps/bsearch.ts @@ -0,0 +1,112 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import request from 'superagent'; +import { inflateResponse } from '@kbn/bfetch-plugin/public/streaming'; +import expect from '@kbn/expect'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { BFETCH_ROUTE_VERSION_LATEST } from '@kbn/bfetch-plugin/common'; +import type { FtrProviderContext } from '../../ftr_provider_context'; + +function parseBfetchResponse(resp: request.Response, compressed: boolean = false) { + return resp.text + .trim() + .split('\n') + .map((item) => { + return JSON.parse(compressed ? inflateResponse(item) : item); + }); +} + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('bsearch', () => { + describe('ES|QL', () => { + it(`should return getColumns response in expected shape`, async () => { + const resp = await supertest + .post(`/internal/bsearch`) + .set('kbn-xsrf', 'kibana') + .set(ELASTIC_HTTP_VERSION_HEADER, BFETCH_ROUTE_VERSION_LATEST) + .send({ + batch: [ + { + request: { + params: { + query: 'from logstash-* | keep geo.coordinates | limit 0', + }, + }, + options: { + strategy: 'esql', + }, + }, + ], + }); + + const jsonBody = parseBfetchResponse(resp); + expect(resp.status).to.be(200); + expect(jsonBody[0].result.rawResponse).to.eql({ + columns: [ + { + name: 'geo.coordinates', + type: 'geo_point', + }, + ], + values: [], + }); + }); + + it(`should return getValues response in expected shape`, async () => { + const resp = await supertest + .post(`/internal/bsearch`) + .set('kbn-xsrf', 'kibana') + .set(ELASTIC_HTTP_VERSION_HEADER, BFETCH_ROUTE_VERSION_LATEST) + .send({ + batch: [ + { + request: { + params: { + dropNullColumns: true, + query: + 'from logstash-* | keep geo.coordinates, @timestamp | sort @timestamp | limit 1', + }, + }, + options: { + strategy: 'esql', + }, + }, + ], + }); + + const jsonBody = parseBfetchResponse(resp); + expect(resp.status).to.be(200); + expect(jsonBody[0].result.rawResponse).to.eql({ + all_columns: [ + { + name: 'geo.coordinates', + type: 'geo_point', + }, + { + name: '@timestamp', + type: 'date', + }, + ], + columns: [ + { + name: 'geo.coordinates', + type: 'geo_point', + }, + { + name: '@timestamp', + type: 'date', + }, + ], + values: [['POINT (-120.9871642 38.68407028)', '2015-09-20T00:00:00.000Z']], + }); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/maps/index.js b/x-pack/test/api_integration/apis/maps/index.js index 438b37ae841c9..fec2cac61950b 100644 --- a/x-pack/test/api_integration/apis/maps/index.js +++ b/x-pack/test/api_integration/apis/maps/index.js @@ -38,6 +38,7 @@ export default function ({ loadTestFile, getService }) { loadTestFile(require.resolve('./migrations')); loadTestFile(require.resolve('./get_tile')); loadTestFile(require.resolve('./get_grid_tile')); + loadTestFile(require.resolve('./bsearch')); }); }); } diff --git a/x-pack/test/api_integration/apis/search/search.ts b/x-pack/test/api_integration/apis/search/search.ts index 15c774eef34ef..bccc7321f8948 100644 --- a/x-pack/test/api_integration/apis/search/search.ts +++ b/x-pack/test/api_integration/apis/search/search.ts @@ -269,26 +269,6 @@ export default function ({ getService }: FtrProviderContext) { verifyErrorResponse(resp.body, 400, 'Request must contain a kbn-xsrf header.'); }); - it('should return 400 when unknown index type is provided', async () => { - const resp = await supertest - .post(`/internal/search/ese`) - .set(ELASTIC_HTTP_VERSION_HEADER, '1') - .set('kbn-xsrf', 'foo') - .send({ - indexType: 'baad', - params: { - body: { - query: { - match_all: {}, - }, - }, - }, - }) - .expect(400); - - verifyErrorResponse(resp.body, 400, 'Unknown indexType'); - }); - it('should return 400 if invalid id is provided', async () => { const resp = await supertest .post(`/internal/search/ese/123`) diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/attachments_framework/external_references.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/attachments_framework/external_references.ts index 53b4e7d94c955..8ae71534ce3cd 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/attachments_framework/external_references.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/attachments_framework/external_references.ts @@ -501,6 +501,7 @@ export default ({ getService }: FtrProviderContext): void => { expect(types).to.eql({ '.files': '559a37324c84f1f2eadcc5bce43115d09501ffe4', '.test': 'ab2204830c67f5cf992c9aa2f7e3ead752cc60a1', + endpoint: 'e13fe41b5c330dd923da91992ed0cedb7e30960f', indicator: 'e1ea6f0518f2e0e4b0b5c0739efe805598cf2516', osquery: '99bee68fce8ee84e81d67c536e063d3e1a2cee96', }); diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/rule_page.ts b/x-pack/test/cloud_security_posture_functional/page_objects/rule_page.ts index 8ab9afd818146..3cf0810c7a03e 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/rule_page.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/rule_page.ts @@ -181,6 +181,10 @@ export function RulePagePageProvider({ getService, getPageObjects }: FtrProvider const disabledRulesButton = await testSubjects.find('rules-counters-disabled-rules-button'); await disabledRulesButton.click(); }, + + doesElementExist: async (selector: string) => { + return await testSubjects.exists(selector); + }, }; const navigateToRulePage = async (benchmarkCisId: string, benchmarkCisVersion: string) => { diff --git a/x-pack/test/cloud_security_posture_functional/pages/rules.ts b/x-pack/test/cloud_security_posture_functional/pages/rules.ts index 81629da46439c..b734c391bf27c 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/rules.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/rules.ts @@ -28,8 +28,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'findings', ]); - // Failing: See https://github.com/elastic/kibana/issues/175905 - describe.skip('Cloud Posture Rules Page', function () { + describe('Cloud Posture Rules Page', function () { this.tags(['cloud_security_posture_rules_page']); let rule: typeof pageObjects.rule; let findings: typeof pageObjects.findings; @@ -72,9 +71,81 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await findings.index.remove(); }); - // FLAKY: https://github.com/elastic/kibana/issues/175614 - describe.skip('Rules Page - Bulk Action buttons', () => { - it('It should disable both Enable and Disable options when there are no rules selected', async () => { + describe('Rules Page - Rules Counters', () => { + it('Shows posture score when there are findings', async () => { + const isEmptyStateVisible = await rule.rulePage.getCountersEmptyState(); + expect(isEmptyStateVisible).to.be(false); + + const postureScoreCounter = await rule.rulePage.getPostureScoreCounter(); + expect((await postureScoreCounter.getVisibleText()).includes('33%')).to.be(true); + }); + + it('Clicking the posture score button leads to the dashboard', async () => { + await rule.rulePage.clickPostureScoreButton(); + await pageObjects.common.waitUntilUrlIncludes('cloud_security_posture/dashboard'); + }); + + it('Shows integrations count when there are findings', async () => { + const integrationsCounter = await rule.rulePage.getIntegrationsEvaluatedCounter(); + expect((await integrationsCounter.getVisibleText()).includes('1')).to.be(true); + }); + + it('Clicking the integrations counter button leads to the integration page', async () => { + await rule.rulePage.clickIntegrationsEvaluatedButton(); + await pageObjects.common.waitUntilUrlIncludes('add-integration/kspm'); + }); + + it('Shows the failed findings counter when there are findings', async () => { + const failedFindingsCounter = await rule.rulePage.getFailedFindingsCounter(); + expect((await failedFindingsCounter.getVisibleText()).includes('2')).to.be(true); + }); + + it('Clicking the failed findings button leads to the findings page', async () => { + await rule.rulePage.clickFailedFindingsButton(); + await pageObjects.common.waitUntilUrlIncludes( + 'cloud_security_posture/findings/configurations' + ); + }); + + it('Shows the disabled rules count', async () => { + const disabledRulesCounter = await rule.rulePage.getDisabledRulesCounter(); + expect((await disabledRulesCounter.getVisibleText()).includes('0')).to.be(true); + + // disable rule 1.1.1 (k8s findings mock contains a findings from that rule) + await rule.rulePage.clickEnableRulesRowSwitchButton(0); + await pageObjects.header.waitUntilLoadingHasFinished(); + expect((await disabledRulesCounter.getVisibleText()).includes('1')).to.be(true); + + const postureScoreCounter = await rule.rulePage.getPostureScoreCounter(); + expect((await postureScoreCounter.getVisibleText()).includes('0%')).to.be(true); + + // enable rule back + await rule.rulePage.clickEnableRulesRowSwitchButton(0); + }); + + it('Clicking the disabled rules button shows enables the disabled filter', async () => { + await rule.rulePage.clickEnableRulesRowSwitchButton(0); + await pageObjects.header.waitUntilLoadingHasFinished(); + + await rule.rulePage.clickDisabledRulesButton(); + await pageObjects.header.waitUntilLoadingHasFinished(); + expect((await rule.rulePage.getEnableRulesRowSwitchButton()) === 1).to.be(true); + }); + + it('Shows empty state when there are no findings', async () => { + // Ensure there are no findings initially + await findings.index.remove(); + await rule.navigateToRulePage('cis_k8s', '1.0.1'); + + const isEmptyStateVisible = await rule.rulePage.getCountersEmptyState(); + expect(isEmptyStateVisible).to.be(true); + await rule.rulePage.clickEnableRulesRowSwitchButton(0); + }); + }); + + describe('Rules Page - Bulk Action buttons', () => { + it('It should disable Enable option when there are all rules selected are already enabled ', async () => { + await rule.rulePage.clickSelectAllRules(); await rule.rulePage.toggleBulkActionButton(); expect( (await rule.rulePage.isBulkActionOptionDisabled(RULES_BULK_ACTION_OPTION_ENABLE)) === @@ -83,11 +154,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect( (await rule.rulePage.isBulkActionOptionDisabled(RULES_BULK_ACTION_OPTION_DISABLE)) === 'true' - ).to.be(true); + ).to.be(false); }); - it('It should disable Enable option when there are all rules selected are already enabled ', async () => { - await rule.rulePage.clickSelectAllRules(); + it('It should disable both Enable and Disable options when there are no rules selected', async () => { await rule.rulePage.toggleBulkActionButton(); expect( (await rule.rulePage.isBulkActionOptionDisabled(RULES_BULK_ACTION_OPTION_ENABLE)) === @@ -96,7 +166,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect( (await rule.rulePage.isBulkActionOptionDisabled(RULES_BULK_ACTION_OPTION_DISABLE)) === 'true' - ).to.be(false); + ).to.be(true); }); it('It should disable Disable option when there are all rules selected are already Disabled', async () => { @@ -178,6 +248,15 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await pageObjects.header.waitUntilLoadingHasFinished(); expect((await rule.rulePage.getEnableSwitchButtonState()) === 'false').to.be(true); }); + it('Alerts section of Rules Flyout shows Disabled text when Rules are disabled', async () => { + await rule.rulePage.clickRulesNames(0); + await pageObjects.header.waitUntilLoadingHasFinished(); + expect( + (await rule.rulePage.doesElementExist( + 'csp:findings-flyout-create-detection-rule-link' + )) === false + ).to.be(true); + }); it('Users are able to Enable/Disable Rule from Take Action on Rule Flyout', async () => { await rule.rulePage.clickRulesNames(0); await rule.rulePage.clickTakeActionButton(); @@ -185,78 +264,14 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await pageObjects.header.waitUntilLoadingHasFinished(); expect((await rule.rulePage.getEnableSwitchButtonState()) === 'true').to.be(true); }); - }); - - describe('Rules Page - Rules Counters', () => { - it('Shows posture score when there are findings', async () => { - const isEmptyStateVisible = await rule.rulePage.getCountersEmptyState(); - expect(isEmptyStateVisible).to.be(false); - - const postureScoreCounter = await rule.rulePage.getPostureScoreCounter(); - expect((await postureScoreCounter.getVisibleText()).includes('33%')).to.be(true); - }); - - it('Clicking the posture score button leads to the dashboard', async () => { - await rule.rulePage.clickPostureScoreButton(); - await pageObjects.common.waitUntilUrlIncludes('cloud_security_posture/dashboard'); - }); - - it('Shows integrations count when there are findings', async () => { - const integrationsCounter = await rule.rulePage.getIntegrationsEvaluatedCounter(); - expect((await integrationsCounter.getVisibleText()).includes('1')).to.be(true); - }); - - it('Clicking the integrations counter button leads to the integration page', async () => { - await rule.rulePage.clickIntegrationsEvaluatedButton(); - await pageObjects.common.waitUntilUrlIncludes( - 'cloud_security_posture/add-integration/kspm' - ); - }); - - it('Shows the failed findings counter when there are findings', async () => { - const failedFindingsCounter = await rule.rulePage.getFailedFindingsCounter(); - expect((await failedFindingsCounter.getVisibleText()).includes('2')).to.be(true); - }); - - it('Clicking the failed findings button leads to the findings page', async () => { - await rule.rulePage.clickFailedFindingsButton(); - await pageObjects.common.waitUntilUrlIncludes( - 'cloud_security_posture/findings/configurations' - ); - }); - - it('Shows the disabled rules count', async () => { - const disabledRulesCounter = await rule.rulePage.getDisabledRulesCounter(); - expect((await disabledRulesCounter.getVisibleText()).includes('0')).to.be(true); - - // disable rule 1.1.1 (k8s findings mock contains a findings from that rule) - await rule.rulePage.clickEnableRulesRowSwitchButton(0); - await pageObjects.header.waitUntilLoadingHasFinished(); - expect((await disabledRulesCounter.getVisibleText()).includes('1')).to.be(true); - - const postureScoreCounter = await rule.rulePage.getPostureScoreCounter(); - expect((await postureScoreCounter.getVisibleText()).includes('0%')).to.be(true); - - // enable rule back - await rule.rulePage.clickEnableRulesRowSwitchButton(0); - }); - - it('Clicking the disabled rules button shows enables the disabled filter', async () => { - await rule.rulePage.clickEnableRulesRowSwitchButton(0); - await pageObjects.header.waitUntilLoadingHasFinished(); - - await rule.rulePage.clickDisabledRulesButton(); + it('Alerts section of Rules Flyout shows Detection Rule Counter component when Rules are enabled', async () => { + await rule.rulePage.clickRulesNames(0); await pageObjects.header.waitUntilLoadingHasFinished(); - expect((await rule.rulePage.getEnableRulesRowSwitchButton()) === 1).to.be(true); - }); - - it('Shows empty state when there are no findings', async () => { - // Ensure there are no findings initially - await findings.index.remove(); - await rule.navigateToRulePage('cis_k8s', '1.0.1'); - - const isEmptyStateVisible = await rule.rulePage.getCountersEmptyState(); - expect(isEmptyStateVisible).to.be(true); + expect( + (await rule.rulePage.doesElementExist( + 'csp:findings-flyout-create-detection-rule-link' + )) === true + ).to.be(true); }); }); }); diff --git a/x-pack/test/fleet_api_integration/apis/data_streams/list.ts b/x-pack/test/fleet_api_integration/apis/data_streams/list.ts index 905d492e29341..5b93affbb82ec 100644 --- a/x-pack/test/fleet_api_integration/apis/data_streams/list.ts +++ b/x-pack/test/fleet_api_integration/apis/data_streams/list.ts @@ -111,26 +111,48 @@ export default function (providerContext: FtrProviderContext) { beforeEach(async () => { await installPackage(pkgName, pkgVersion); + + // Create index template that do use final pipeline + const sourceIndexTemplate = ( + await es.indices.getIndexTemplate({ + name: logsTemplateName, + }) + ).index_templates[0].index_template; + await es.indices.putIndexTemplate({ + name: `${logsTemplateName}-testwithoutfinalpipeline`, + template: sourceIndexTemplate.template, + _meta: sourceIndexTemplate._meta, + data_stream: sourceIndexTemplate.data_stream, + composed_of: sourceIndexTemplate.composed_of.filter( + (template) => template.includes('@settings') || template.includes('@package') + ), + index_patterns: [`${logsTemplateName}-testwithoutfinalpipeline`], + priority: 500, + }); }); afterEach(async () => { - await uninstallPackage(pkgName, pkgVersion); - try { - await es.transport.request({ - method: 'DELETE', - path: `/_data_stream/${logsTemplateName}-default`, - }); - await es.transport.request({ - method: 'DELETE', - path: `/_data_stream/${metricsTemplateName}-default`, - }); - await es.transport.request({ - method: 'DELETE', - path: `/_data_stream/${notFleetTemplateName}-default`, - }); - } catch (e) { - // Silently swallow errors here as not all tests seed data streams + const pathsToDelete = [ + `/_data_stream/${logsTemplateName}-default`, + `/_data_stream/${metricsTemplateName}-default`, + `/_data_stream/${notFleetTemplateName}-default`, + `/_data_stream/${logsTemplateName}-testwithoutfinalpipeline`, + ]; + + for (const path of pathsToDelete) { + await es.transport + .request({ + method: 'DELETE', + path, + }) + // Silently swallow errors here as not all tests seed data streams + .catch((e) => {}); } + + await es.indices.deleteIndexTemplate({ + name: `${logsTemplateName}-testwithoutfinalpipeline`, + }); + await uninstallPackage(pkgName, pkgVersion); }); it("should return no data streams when there isn't any data yet", async function () { @@ -196,6 +218,40 @@ export default function (providerContext: FtrProviderContext) { }); }); + it('should work for datastream without event.ingested and use @timestamp for last_activity_ms', async function () { + const timestamp = Date.now() - 1000 * 60 * 60; + + await es.index({ + index: `${logsTemplateName}-testwithoutfinalpipeline`, + refresh: 'wait_for', + + document: { + '@timestamp': new Date(timestamp).toISOString(), + logs_test_name: 'test', + data_stream: { + dataset: `${pkgName}.test_logs`, + namespace: 'testwithoutfinalpipeline', + type: 'logs', + }, + }, + }); + + const res = await es.search({ + index: `${logsTemplateName}-testwithoutfinalpipeline`, + }); + + expect(res.hits.hits.length).to.eql(1); + expect((res.hits.hits[0]._source as any).event).to.eql(undefined); + + const { body } = await getDataStreams(); + expect(body.data_streams.length).to.eql(1); + + const dataStream = body.data_streams[0]; + + expect(dataStream.dataset).to.eql('datastreams.test_logs'); + expect(dataStream.last_activity_ms).to.eql(timestamp); + }); + it('should return correct number of data streams regardless of number of backing indices', async function () { await seedDataStreams(); await retry.tryForTime(10000, async () => { diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis.ts index 30c61a58c0545..9799d4418d729 100644 --- a/x-pack/test/functional/apps/aiops/log_rate_analysis.ts +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis.ts @@ -36,7 +36,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await ml.jobSourceSelection.selectSourceForLogRateAnalysis(testData.sourceIndexOrSavedSearch); }); - it(`${testData.suiteTitle} displays index details`, async () => { + // FLAKY: https://github.com/elastic/kibana/issues/176387 + it.skip(`${testData.suiteTitle} displays index details`, async () => { await ml.testExecution.logTestStep(`${testData.suiteTitle} displays the time range step`); await aiops.logRateAnalysisPage.assertTimeRangeSelectorSectionExists(); @@ -274,8 +275,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); } - // FLAKY: https://github.com/elastic/kibana/issues/176066 - describe.skip('log rate analysis', async function () { + describe('log rate analysis', async function () { for (const testData of logRateAnalysisTestData) { describe(`with '${testData.sourceIndexOrSavedSearch}'`, function () { before(async () => { diff --git a/x-pack/test/functional/apps/discover/saved_searches.ts b/x-pack/test/functional/apps/discover/saved_searches.ts index c95249e927084..8f5fe5dc9bc11 100644 --- a/x-pack/test/functional/apps/discover/saved_searches.ts +++ b/x-pack/test/functional/apps/discover/saved_searches.ts @@ -45,8 +45,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.common.unsetTime(); }); - // FLAKY: https://github.com/elastic/kibana/issues/104578 - describe.skip('Customize time range', () => { + describe('Customize time range', () => { it('should be possible to customize time range for saved searches on dashboards', async () => { await PageObjects.dashboard.navigateToApp(); await PageObjects.dashboard.clickNewDashboard(); diff --git a/x-pack/test/functional/apps/managed_content/managed_content.ts b/x-pack/test/functional/apps/managed_content/managed_content.ts index a42d5b5a0c94c..545d7ad1c4dee 100644 --- a/x-pack/test/functional/apps/managed_content/managed_content.ts +++ b/x-pack/test/functional/apps/managed_content/managed_content.ts @@ -16,10 +16,12 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'common', 'discover', 'maps', + 'dashboard', ]); const kibanaServer = getService('kibanaServer'); const esArchiver = getService('esArchiver'); const testSubjects = getService('testSubjects'); + const dashboardAddPanel = getService('dashboardAddPanel'); describe('Managed Content', () => { before(async () => { @@ -32,19 +34,19 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/managed_content'); }); - const expectManagedContentSignifiers = async ( - expected: boolean, - saveButtonTestSubject: string - ) => { - await testSubjects[expected ? 'existOrFail' : 'missingOrFail']('managedContentBadge'); - await testSubjects.click(saveButtonTestSubject); - - const saveAsNewCheckbox = await testSubjects.find('saveAsNewCheckbox'); - expect(await testSubjects.isEuiSwitchChecked(saveAsNewCheckbox)).to.be(expected); - expect(await saveAsNewCheckbox.getAttribute('disabled')).to.be(expected ? 'true' : null); - }; - describe('preventing the user from overwriting managed content', () => { + const expectManagedContentSignifiers = async ( + expected: boolean, + saveButtonTestSubject: string + ) => { + await testSubjects[expected ? 'existOrFail' : 'missingOrFail']('managedContentBadge'); + await testSubjects.click(saveButtonTestSubject); + + const saveAsNewCheckbox = await testSubjects.find('saveAsNewCheckbox'); + expect(await testSubjects.isEuiSwitchChecked(saveAsNewCheckbox)).to.be(expected); + expect(await saveAsNewCheckbox.getAttribute('disabled')).to.be(expected ? 'true' : null); + }; + it('lens', async () => { await PageObjects.common.navigateToActualUrl( 'lens', @@ -64,60 +66,105 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await expectManagedContentSignifiers(false, 'lnsApp_saveButton'); }); - }); - it('discover', async () => { - await PageObjects.common.navigateToActualUrl( - 'discover', - 'view/managed-3d62-4113-ac7c-de2e20a68fbc' - ); - await PageObjects.discover.waitForDiscoverAppOnScreen(); + it('discover', async () => { + await PageObjects.common.navigateToActualUrl( + 'discover', + 'view/managed-3d62-4113-ac7c-de2e20a68fbc' + ); + await PageObjects.discover.waitForDiscoverAppOnScreen(); + + await expectManagedContentSignifiers(true, 'discoverSaveButton'); + + await PageObjects.common.navigateToActualUrl( + 'discover', + 'view/unmanaged-3d62-4113-ac7c-de2e20a68fbc' + ); + await PageObjects.discover.waitForDiscoverAppOnScreen(); + + await expectManagedContentSignifiers(false, 'discoverSaveButton'); + }); - await expectManagedContentSignifiers(true, 'discoverSaveButton'); + it('visualize', async () => { + await PageObjects.common.navigateToActualUrl( + 'visualize', + 'edit/managed-feb9-4ba6-9538-1b8f67fb4f57' + ); + await PageObjects.visChart.waitForVisualization(); - await PageObjects.common.navigateToActualUrl( - 'discover', - 'view/unmanaged-3d62-4113-ac7c-de2e20a68fbc' - ); - await PageObjects.discover.waitForDiscoverAppOnScreen(); + await expectManagedContentSignifiers(true, 'visualizeSaveButton'); - await expectManagedContentSignifiers(false, 'discoverSaveButton'); - }); + await PageObjects.common.navigateToActualUrl( + 'visualize', + 'edit/unmanaged-feb9-4ba6-9538-1b8f67fb4f57' + ); + await PageObjects.visChart.waitForVisualization(); + + await expectManagedContentSignifiers(false, 'visualizeSaveButton'); + }); - it('visualize', async () => { - await PageObjects.common.navigateToActualUrl( - 'visualize', - 'edit/managed-feb9-4ba6-9538-1b8f67fb4f57' - ); - await PageObjects.visChart.waitForVisualization(); + it('maps', async () => { + await PageObjects.common.navigateToActualUrl( + 'maps', + 'map/managed-d7ab-46eb-a807-8fed28ed8566' + ); + await PageObjects.maps.waitForLayerAddPanelClosed(); - await expectManagedContentSignifiers(true, 'visualizeSaveButton'); + await expectManagedContentSignifiers(true, 'mapSaveButton'); - await PageObjects.common.navigateToActualUrl( - 'visualize', - 'edit/unmanaged-feb9-4ba6-9538-1b8f67fb4f57' - ); - await PageObjects.visChart.waitForVisualization(); + await PageObjects.common.navigateToActualUrl( + 'maps', + 'map/unmanaged-d7ab-46eb-a807-8fed28ed8566' + ); + await PageObjects.maps.waitForLayerAddPanelClosed(); - await expectManagedContentSignifiers(false, 'visualizeSaveButton'); + await expectManagedContentSignifiers(false, 'mapSaveButton'); + }); }); - it('maps', async () => { - await PageObjects.common.navigateToActualUrl( - 'maps', - 'map/managed-d7ab-46eb-a807-8fed28ed8566' - ); - await PageObjects.maps.waitForLayerAddPanelClosed(); + describe('managed panels in dashboards', () => { + it('inlines panels when managed dashboard cloned', async () => { + await PageObjects.common.navigateToActualUrl( + 'dashboard', + 'view/c44c86f9-b105-4a9c-9a24-449a58a827f3' + ); + + await PageObjects.dashboard.waitForRenderComplete(); + + await PageObjects.dashboard.clickClone(); - await expectManagedContentSignifiers(true, 'mapSaveButton'); + await PageObjects.dashboard.waitForRenderComplete(); - await PageObjects.common.navigateToActualUrl( - 'maps', - 'map/unmanaged-d7ab-46eb-a807-8fed28ed8566' - ); - await PageObjects.maps.waitForLayerAddPanelClosed(); + await testSubjects.missingOrFail('embeddablePanelNotification-ACTION_LIBRARY_NOTIFICATION'); + }); + + it('adds managed panels by-value', async () => { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.gotoDashboardLandingPage(); + await PageObjects.dashboard.clickNewDashboard(); + + await dashboardAddPanel.addEmbeddables([ + { name: 'Managed lens vis', type: 'lens' }, + { name: 'Managed legacy visualization', type: 'visualization' }, + { name: 'Managed map', type: 'map' }, + { name: 'Managed saved search', type: 'search' }, + ]); + + await testSubjects.missingOrFail('embeddablePanelNotification-ACTION_LIBRARY_NOTIFICATION'); + + await dashboardAddPanel.addEmbeddables([ + { name: 'Unmanaged lens vis', type: 'lens' }, + { name: 'Unmanaged legacy visualization', type: 'visualization' }, + { name: 'Unmanaged map', type: 'map' }, + { name: 'Unmanaged saved search', type: 'search' }, + ]); + + const byRefSignifiers = await testSubjects.findAll( + 'embeddablePanelNotification-ACTION_LIBRARY_NOTIFICATION' + ); - await expectManagedContentSignifiers(false, 'mapSaveButton'); + expect(byRefSignifiers.length).to.be(4); + }); }); }); } diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/anomaly_explorer.ts b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/anomaly_explorer.ts index a6e867676d10f..76bed212eebde 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/anomaly_explorer.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/anomaly_explorer.ts @@ -543,6 +543,39 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); }); }); + + describe('Use anomaly table action to view in Discover', function () { + beforeEach(async () => { + await ml.navigation.navigateToAnomalyExplorer( + testData.jobConfig.job_id, + { + from: '2016-02-07T00%3A00%3A00.000Z', + to: '2016-02-11T23%3A59%3A54.000Z', + }, + () => elasticChart.setNewChartUiDebugFlag(true) + ); + + await ml.commonUI.waitForMlLoadingIndicatorToDisappear(); + await ml.commonUI.waitForDatePickerIndicatorLoaded(); + await ml.swimLane.waitForSwimLanesToLoad(); + }); + + it('should render the anomaly table', async () => { + await ml.testExecution.logTestStep('displays the anomalies table'); + await ml.anomaliesTable.assertTableExists(); + + await ml.testExecution.logTestStep('anomalies table is not empty'); + await ml.anomaliesTable.assertTableNotEmpty(); + }); + + it('should click the Discover action in the anomaly table', async () => { + await ml.anomaliesTable.assertAnomalyActionsMenuButtonExists(0); + await ml.anomaliesTable.scrollRowIntoView(0); + await ml.anomaliesTable.assertAnomalyActionsMenuButtonEnabled(0, true); + await ml.anomaliesTable.assertAnomalyActionDiscoverButtonExists(0); + await ml.anomaliesTable.ensureAnomalyActionDiscoverButtonClicked(0); + }); + }); }); } }); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/single_metric_viewer.ts b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/single_metric_viewer.ts index 1779589a5a0c1..c9ceb71459e4a 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/single_metric_viewer.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/single_metric_viewer.ts @@ -90,6 +90,13 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('anomalies table is not empty'); await ml.anomaliesTable.assertTableNotEmpty(); }); + + it('should click on an anomaly marker', async () => { + await ml.singleMetricViewer.assertAnomalyMarkerExist(); + await ml.singleMetricViewer.openAnomalyMarkerActionsPopover(); + await ml.anomaliesTable.assertAnomalyActionDiscoverButtonExists(0); + await ml.anomaliesTable.ensureAnomalyActionDiscoverButtonClicked(0); + }); }); describe('with entity fields', function () { @@ -193,7 +200,9 @@ export default function ({ getService }: FtrProviderContext) { // Also sorting by name is enforced because the model plot is enabled // and anomalous only is disabled await ml.singleMetricViewer.assertEntityConfig('day_of_week', false, 'name', 'desc'); + }); + it('should render the singe metric viewer chart and anomaly table', async () => { await ml.testExecution.logTestStep('displays the chart'); await ml.singleMetricViewer.assertChartExist(); @@ -203,6 +212,14 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('anomalies table is not empty'); await ml.anomaliesTable.assertTableNotEmpty(); }); + + it('should click the Discover action in the anomaly table', async () => { + await ml.anomaliesTable.assertAnomalyActionsMenuButtonExists(0); + await ml.anomaliesTable.scrollRowIntoView(0); + await ml.anomaliesTable.assertAnomalyActionsMenuButtonEnabled(0, true); + await ml.anomaliesTable.assertAnomalyActionDiscoverButtonExists(0); + await ml.anomaliesTable.ensureAnomalyActionDiscoverButtonClicked(0); + }); }); }); } diff --git a/x-pack/test/functional/services/ml/anomalies_table.ts b/x-pack/test/functional/services/ml/anomalies_table.ts index 52eaf5715f673..c59221289f848 100644 --- a/x-pack/test/functional/services/ml/anomalies_table.ts +++ b/x-pack/test/functional/services/ml/anomalies_table.ts @@ -131,6 +131,24 @@ export function MachineLearningAnomaliesTableProvider({ getService }: FtrProvide ); }, + async assertAnomalyActionDiscoverButtonExists(rowIndex: number) { + await this.ensureAnomalyActionsMenuOpen(rowIndex); + await testSubjects.existOrFail('mlAnomaliesListRowAction_viewInDiscoverButton'); + }, + + async assertAnomalyActionDiscoverButtonNotExists(rowIndex: number) { + await this.ensureAnomalyActionsMenuOpen(rowIndex); + await testSubjects.missingOrFail('mlAnomaliesListRowAction_viewInDiscoverButton'); + }, + + async ensureAnomalyActionDiscoverButtonClicked(rowIndex: number) { + await retry.tryForTime(10 * 1000, async () => { + await this.ensureAnomalyActionsMenuOpen(rowIndex); + await testSubjects.click('mlAnomaliesListRowAction_viewInDiscoverButton'); + await testSubjects.existOrFail('discoverLayoutResizableContainer'); + }); + }, + async assertAnomalyActionLogRateAnalysisButtonExists(rowIndex: number) { await this.ensureAnomalyActionsMenuOpen(rowIndex); await testSubjects.existOrFail('mlAnomaliesListRowAction_runLogRateAnalysisButton'); diff --git a/x-pack/test/functional/services/ml/single_metric_viewer.ts b/x-pack/test/functional/services/ml/single_metric_viewer.ts index 29f1ded74deba..05ae2bd20cab7 100644 --- a/x-pack/test/functional/services/ml/single_metric_viewer.ts +++ b/x-pack/test/functional/services/ml/single_metric_viewer.ts @@ -73,6 +73,15 @@ export function MachineLearningSingleMetricViewerProvider( await testSubjects.existOrFail('mlSingleMetricViewerChart'); }, + async assertAnomalyMarkerExist() { + await testSubjects.existOrFail('mlAnomalyMarker'); + }, + + async openAnomalyMarkerActionsPopover() { + await testSubjects.click('mlAnomalyMarker'); + await testSubjects.existOrFail('mlAnomaliesListRowActionsMenu'); + }, + async assertAnnotationsExists(state: string) { await testSubjects.existOrFail(`mlAnomalyExplorerAnnotations ${state}`, { timeout: 30 * 1000, diff --git a/x-pack/test/security_solution_api_integration/package.json b/x-pack/test/security_solution_api_integration/package.json index 1e2b5e4c7a509..fd8751b8c1820 100644 --- a/x-pack/test/security_solution_api_integration/package.json +++ b/x-pack/test/security_solution_api_integration/package.json @@ -5,6 +5,9 @@ "private": true, "license": "Elastic License 2.0", "scripts": { + "initialize-server:genai": "node ./scripts/index.js server genai trial_license_complete_tier", + "run-tests:genai": "node ./scripts/index.js runner genai trial_license_complete_tier", + "initialize-server:ea": "node ./scripts/index.js server entity_analytics trial_license_complete_tier", "run-tests:ea": "node ./scripts/index.js runner entity_analytics trial_license_complete_tier", @@ -26,6 +29,12 @@ "initialize-server:lists:complete": "node ./scripts/index.js server lists_and_exception_lists trial_license_complete_tier", "run-tests:lists:complete": "node ./scripts/index.js runner lists_and_exception_lists trial_license_complete_tier", + "genai:server:serverless": "npm run initialize-server:genai invoke_ai serverless", + "genai:runner:serverless": "npm run run-tests:genai invoke_ai serverless serverlessEnv", + "genai:qa:serverless": "npm run run-tests:genai invoke_ai serverless qaEnv", + "genai:server:ess": "npm run initialize-server:genai invoke_ai ess", + "genai:runner:ess": "npm run run-tests:genai invoke_ai ess essEnv", + "entity_analytics:server:serverless": "npm run initialize-server:ea risk_engine serverless", "entity_analytics:runner:serverless": "npm run run-tests:ea risk_engine serverless serverlessEnv", "entity_analytics:qa:serverless": "npm run run-tests:ea risk_engine serverless qaEnv", diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/trial_license_complete_tier/alerts_compatibility.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/trial_license_complete_tier/alerts_compatibility.ts index 5547bf57fb1ae..25ef93cafaac0 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/trial_license_complete_tier/alerts_compatibility.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/trial_license_complete_tier/alerts_compatibility.ts @@ -636,7 +636,8 @@ export default ({ getService }: FtrProviderContext) => { }); }); - describe('Threshold', () => { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/176359 + describe.skip('Threshold', () => { beforeEach(async () => { await esArchiver.load('x-pack/test/functional/es_archives/security_solution/alerts/7.16.0'); await createAlertsIndex(supertest, log); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/trial_license_complete_tier/role_based_rule_exceptions_workflows.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/trial_license_complete_tier/role_based_rule_exceptions_workflows.ts index ef78015ef7715..e308732db3821 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/trial_license_complete_tier/role_based_rule_exceptions_workflows.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/trial_license_complete_tier/role_based_rule_exceptions_workflows.ts @@ -803,7 +803,7 @@ export default ({ getService }: FtrProviderContext) => { }); it('generates no alerts when a value list exception is added for a query rule', async () => { - const valueListId = 'value-list-id'; + const valueListId = 'value-list-id.txt'; await importFile(supertest, log, 'keyword', ['suricata-sensor-amsterdam'], valueListId); const rule: QueryRuleCreateProps = { name: 'Simple Rule Query', @@ -835,7 +835,7 @@ export default ({ getService }: FtrProviderContext) => { }); it('generates no alerts when a value list exception is added for a threat match rule', async () => { - const valueListId = 'value-list-id'; + const valueListId = 'value-list-id.txt'; await importFile(supertest, log, 'keyword', ['zeek-sensor-amsterdam'], valueListId); const rule: ThreatMatchRuleCreateProps = { description: 'Detecting root and admin users', @@ -883,7 +883,7 @@ export default ({ getService }: FtrProviderContext) => { }); it('generates no alerts when a value list exception is added for a threshold rule', async () => { - const valueListId = 'value-list-id'; + const valueListId = 'value-list-id.txt'; await importFile(supertest, log, 'keyword', ['zeek-sensor-amsterdam'], valueListId); const rule: ThresholdRuleCreateProps = { description: 'Detecting root and admin users', @@ -920,7 +920,7 @@ export default ({ getService }: FtrProviderContext) => { }); it('generates no alerts when a value list exception is added for an EQL rule', async () => { - const valueListId = 'value-list-id'; + const valueListId = 'value-list-id.txt'; await importFile(supertest, log, 'keyword', ['zeek-sensor-amsterdam'], valueListId); const rule: EqlRuleCreateProps = { ...getEqlRuleForAlertTesting(['auditbeat-*']), @@ -944,7 +944,7 @@ export default ({ getService }: FtrProviderContext) => { expect(alertsOpen.hits.hits.length).toEqual(0); }); it('should Not allow deleting value list when there are references and ignoreReferences is false', async () => { - const valueListId = 'value-list-id'; + const valueListId = 'value-list-id.txt'; await importFile(supertest, log, 'keyword', ['suricata-sensor-amsterdam'], valueListId); const rule: QueryRuleCreateProps = { ...getSimpleRule(), diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/trial_license_complete_tier/rule_exception_synchronizations.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/trial_license_complete_tier/rule_exception_synchronizations.ts index c584ec46f2ef1..5a68270e1220a 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/trial_license_complete_tier/rule_exception_synchronizations.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/trial_license_complete_tier/rule_exception_synchronizations.ts @@ -107,7 +107,7 @@ export default ({ getService }: FtrProviderContext) => { it('should Not allow editing an Exception with deleted ValueList', async () => { await createListsIndex(supertest, log); - const valueListId = 'value-list-id'; + const valueListId = 'value-list-id.txt'; await importFile(supertest, log, 'keyword', ['suricata-sensor-amsterdam'], valueListId); const rule: QueryRuleCreateProps = { ...getSimpleRule(), diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/machine_learning.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/machine_learning.ts index e222f1ddd7cb4..60ad53f94937f 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/machine_learning.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/machine_learning.ts @@ -229,7 +229,7 @@ export default ({ getService }: FtrProviderContext) => { }); it('generates no alerts when a value list exception is added for an ML rule', async () => { - const valueListId = 'value-list-id'; + const valueListId = 'value-list-id.txt'; await importFile(supertest, log, 'keyword', ['mothra'], valueListId); const { previewId } = await previewRuleWithExceptionEntries({ supertest, diff --git a/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/trial_license_complete_tier/basic.ts b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/trial_license_complete_tier/basic.ts new file mode 100644 index 0000000000000..bcac95217c280 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/trial_license_complete_tier/basic.ts @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { BedrockSimulator } from '@kbn/actions-simulators-plugin/server/bedrock_simulation'; +import { OpenAISimulator } from '@kbn/actions-simulators-plugin/server/openai_simulation'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; +import { postActionsClientExecute } from '../utils/post_actions_client_execute'; +import { ObjectRemover } from '../utils/object_remover'; +import { createConnector } from '../utils/create_connector'; + +const mockRequest = { + params: { + subActionParams: { + messages: [ + { role: 'user', content: '\\n\\n\\n\\nWhat is my name?' }, + { + role: 'assistant', + content: + "I'm sorry, but I don't have the information about your name. You can tell me your name if you'd like, and we can continue our conversation from there.", + }, + { role: 'user', content: '\\n\\nMy name is Andrew' }, + { + role: 'assistant', + content: "Hello, Andrew! It's nice to meet you. What would you like to talk about today?", + }, + { role: 'user', content: '\\n\\nDo you know my name?' }, + ], + }, + subAction: 'invokeAI', + }, + isEnabledKnowledgeBase: false, + isEnabledRAGAlerts: false, + llmType: 'bedrock', +}; + +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const objectRemover = new ObjectRemover(supertest); + const configService = getService('config'); + + // @skipInQA tag because the simulators do not work in the QA env + describe('@ess @serverless @skipInQA Basic Security AI Assistant Invoke AI [non-streaming, non-LangChain]', async () => { + after(() => { + objectRemover.removeAll(); + }); + + describe('With Bedrock connector', () => { + const simulator = new BedrockSimulator({ + proxy: { + config: configService.get('kbnTestServer.serverArgs'), + }, + }); + let apiUrl: string; + let bedrockActionId: string; + + before(async () => { + apiUrl = await simulator.start(); + bedrockActionId = await createConnector(supertest, objectRemover, apiUrl, 'bedrock'); + }); + + after(() => { + simulator.close(); + }); + it('should execute a chat completion', async () => { + const response = await postActionsClientExecute(bedrockActionId, mockRequest, supertest); + + const expected = { + connector_id: bedrockActionId, + data: 'Hello there! How may I assist you today?', + status: 'ok', + }; + + expect(response.body).to.eql(expected); + }); + }); + + describe('With OpenAI connector', () => { + const simulator = new OpenAISimulator({ + returnError: false, + proxy: { + config: configService.get('kbnTestServer.serverArgs'), + }, + }); + let apiUrl: string; + let openaiActionId: string; + + before(async () => { + apiUrl = await simulator.start(); + openaiActionId = await createConnector(supertest, objectRemover, apiUrl, 'openai'); + }); + + after(() => { + simulator.close(); + }); + it('should execute a chat completion', async () => { + const response = await postActionsClientExecute( + openaiActionId, + { ...mockRequest, llmType: 'openai' }, + supertest + ); + + const expected = { + connector_id: openaiActionId, + data: 'Hello there! How may I assist you today?', + status: 'ok', + }; + + expect(response.body).to.eql(expected); + }); + }); + }); +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/trial_license_complete_tier/configs/ess.config.ts b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/trial_license_complete_tier/configs/ess.config.ts new file mode 100644 index 0000000000000..722b39a700026 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/trial_license_complete_tier/configs/ess.config.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrConfigProviderContext } from '@kbn/test'; +import getPort from 'get-port'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile( + require.resolve('../../../../../config/ess/config.base.trial') + ); + + const proxyPort = await getPort({ port: getPort.makeRange(6200, 6299) }); + + return { + ...functionalConfig.getAll(), + kbnTestServer: { + ...functionalConfig.get('kbnTestServer'), + serverArgs: [ + ...functionalConfig.get('kbnTestServer.serverArgs'), + // used for connector simulators + `--xpack.actions.proxyUrl=http://localhost:${proxyPort}`, + `--xpack.actions.enabledActionTypes=${JSON.stringify(['.bedrock', '.gen-ai'])}`, + ], + }, + testFiles: [require.resolve('..')], + junit: { + reportName: 'GenAI - Invoke AI Tests - ESS Env - Trial License', + }, + }; +} diff --git a/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/trial_license_complete_tier/configs/serverless.config.ts b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/trial_license_complete_tier/configs/serverless.config.ts new file mode 100644 index 0000000000000..d0666e224e4bf --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/trial_license_complete_tier/configs/serverless.config.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 { createTestConfig } from '../../../../../config/serverless/config.base'; + +export default createTestConfig({ + kbnTestServerArgs: [ + // used for connector simulators + `--xpack.actions.proxyUrl=http://localhost:6200`, + ], + testFiles: [require.resolve('..')], + junit: { + reportName: 'GenAI - Invoke AI Tests - Serverless Env - Complete Tier', + }, +}); diff --git a/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/trial_license_complete_tier/index.ts b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/trial_license_complete_tier/index.ts new file mode 100644 index 0000000000000..e6ff5bbbfe667 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/trial_license_complete_tier/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + // this is the test suite for the inaptly named post_actions_connector_execute route + describe('GenAI - Invoke AI', function () { + loadTestFile(require.resolve('./basic')); + }); +} diff --git a/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/create_connector.ts b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/create_connector.ts new file mode 100644 index 0000000000000..ac2d60ad631e2 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/create_connector.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 type SuperTest from 'supertest'; +import { + ELASTIC_HTTP_VERSION_HEADER, + X_ELASTIC_INTERNAL_ORIGIN_REQUEST, +} from '@kbn/core-http-common'; +import { getUrlPrefix } from './space_test_utils'; +import { ObjectRemover } from './object_remover'; + +const connectorSetup = { + bedrock: { + connectorTypeId: '.bedrock', + name: 'A bedrock action', + secrets: { + accessKey: 'bedrockAccessKey', + secret: 'bedrockSecret', + }, + config: { + defaultModel: 'anthropic.claude-v2', + }, + }, + openai: { + connectorTypeId: '.gen-ai', + name: 'An openai action', + secrets: { + apiKey: 'genAiApiKey', + }, + config: { + apiProvider: 'OpenAI', + }, + }, +}; + +/** + * Creates a connector + * @param supertest The supertest agent. + * @param apiUrl The url of the api + * @param connectorType The type of connector to create + * @param spaceId The space id + */ +export const createConnector = async ( + supertest: SuperTest.SuperTest, + objectRemover: ObjectRemover, + apiUrl: string, + connectorType: 'bedrock' | 'openai', + spaceId?: string +) => { + const { connectorTypeId, config, name, secrets } = connectorSetup[connectorType]; + const result = await supertest + .post(`${getUrlPrefix(spaceId ?? 'default')}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send({ + name, + connector_type_id: connectorTypeId, + config: { ...config, apiUrl }, + secrets, + }) + .expect(200); + + const { body } = result; + + objectRemover.add(spaceId ?? 'default', body.id, 'connector', 'actions'); + + return body.id; +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/object_remover.ts b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/object_remover.ts new file mode 100644 index 0000000000000..93eaa85e802fc --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/object_remover.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + ELASTIC_HTTP_VERSION_HEADER, + X_ELASTIC_INTERNAL_ORIGIN_REQUEST, +} from '@kbn/core-http-common'; +import { getUrlPrefix } from './space_test_utils'; + +interface ObjectToRemove { + spaceId: string; + id: string; + type: string; + plugin: string; + isInternal?: boolean; +} + +export class ObjectRemover { + private readonly supertest: any; + private objectsToRemove: ObjectToRemove[] = []; + + constructor(supertest: any) { + this.supertest = supertest; + } + + add( + spaceId: ObjectToRemove['spaceId'], + id: ObjectToRemove['id'], + type: ObjectToRemove['type'], + plugin: ObjectToRemove['plugin'], + isInternal?: ObjectToRemove['isInternal'] + ) { + this.objectsToRemove.push({ spaceId, id, type, plugin, isInternal }); + } + + async removeAll() { + await Promise.all( + this.objectsToRemove.map(({ spaceId, id, type, plugin, isInternal }) => { + return this.supertest + .delete( + `${getUrlPrefix(spaceId)}/${isInternal ? 'internal' : 'api'}/${plugin}/${type}/${id}` + ) + .set('kbn-xsrf', 'foo') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .expect(plugin === 'saved_objects' ? 200 : 204); + }) + ); + this.objectsToRemove = []; + } +} diff --git a/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/post_actions_client_execute.ts b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/post_actions_client_execute.ts new file mode 100644 index 0000000000000..0477401a26533 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/post_actions_client_execute.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type SuperTest from 'supertest'; +import { + ELASTIC_HTTP_VERSION_HEADER, + X_ELASTIC_INTERNAL_ORIGIN_REQUEST, +} from '@kbn/core-http-common'; +import { Response } from 'superagent'; + +/** + * Executes an invoke AI action + * @param connectorId The connector id + * @param args The arguments to pass to the action + * @param supertest The supertest agent + */ +export const postActionsClientExecute = async ( + connectorId: string, + args: any, + supertest: SuperTest.SuperTest +): Promise => { + const response = await supertest + .post(`/internal/elastic_assistant/actions/connector/${connectorId}/_execute`) + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(args); + + return response; +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/space_test_utils.ts b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/space_test_utils.ts new file mode 100644 index 0000000000000..6ffbdc492aee4 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/space_test_utils.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export function getUrlPrefix(spaceId: string) { + return spaceId && spaceId !== 'default' ? `/s/${spaceId}` : ``; +} diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/import_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/import_list_items.ts index 99a96ae9052b1..7fabd749bc01d 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/import_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/import_list_items.ts @@ -54,6 +54,39 @@ export default ({ getService }: FtrProviderContext): void => { await deleteListsIndex(supertest, log); }); + it('should not import a list item if the imported file is not .txt or .csv', async () => { + const { body } = await supertest + .post(`${LIST_ITEM_URL}/_import?type=ip`) + .set('kbn-xsrf', 'true') + .attach('file', getImportListItemAsBuffer(['127.0.0.1', '127.0.0.2']), 'list_items.pdf') + .expect('Content-Type', 'application/json; charset=utf-8') + .expect(415); + + expect(body).to.eql({ + status_code: 415, + message: 'Unsupported media type. File must be one of the following types: [.csv, .txt]', + }); + }); + + it('should not import a list item if the imported file exceed the file size limit', async () => { + const { body } = await supertest + .post(`${LIST_ITEM_URL}/_import?type=ip`) + .set('kbn-xsrf', 'true') + .attach( + 'file', + getImportListItemAsBuffer(Array(1000000).fill('127.0.0.1')), + 'list_items.txt' + ) + .expect('Content-Type', 'application/json; charset=utf-8') + .expect(413); + + expect(body).to.eql({ + statusCode: 413, + error: 'Request Entity Too Large', + message: 'Payload content length greater than maximum allowed: 9000000', + }); + }); + it('should set the response content types to be expected when importing two items', async () => { await supertest .post(`${LIST_ITEM_URL}/_import?type=ip`) diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/detection_alerts/assignments/assignments.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/detection_alerts/assignments/assignments.cy.ts index 5dbf14796c583..21a67b7fb4ea4 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/detection_alerts/assignments/assignments.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/detection_alerts/assignments/assignments.cy.ts @@ -42,8 +42,7 @@ import { } from '../../../../../tasks/alert_assignments'; import { ALERTS_COUNT } from '../../../../../screens/alerts'; -// Failing: See https://github.com/elastic/kibana/issues/173429 -describe.skip('Alert user assignment - ESS & Serverless', { tags: ['@ess', '@serverless'] }, () => { +describe('Alert user assignment - ESS & Serverless', { tags: ['@ess', '@serverless'] }, () => { before(() => { cy.task('esArchiverLoad', { archiveName: 'auditbeat_multiple' }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/detection_alerts/assignments/assignments_serverless_complete.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/detection_alerts/assignments/assignments_serverless_complete.cy.ts index 72afcb304f893..f026e4b2262e4 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/detection_alerts/assignments/assignments_serverless_complete.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/detection_alerts/assignments/assignments_serverless_complete.cy.ts @@ -20,8 +20,7 @@ import { loadPageAs, } from '../../../../../tasks/alert_assignments'; -// FLAKY: https://github.com/elastic/kibana/issues/172557 -describe.skip( +describe( 'Alert user assignment - Serverless Complete', { tags: ['@serverless'], diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/detection_alerts/assignments/assignments_serverless_essentials.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/detection_alerts/assignments/assignments_serverless_essentials.cy.ts index 5ae60a01a0e8b..f8bbf2620a542 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/detection_alerts/assignments/assignments_serverless_essentials.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/detection_alerts/assignments/assignments_serverless_essentials.cy.ts @@ -20,8 +20,7 @@ import { loadPageAs, } from '../../../../../tasks/alert_assignments'; -// FLAKY: https://github.com/elastic/kibana/issues/172520 -describe.skip( +describe( 'Alert user assignment - Serverless Essentials', { tags: ['@serverless'], diff --git a/x-pack/test_serverless/api_integration/test_suites/common/search_xpack/search.ts b/x-pack/test_serverless/api_integration/test_suites/common/search_xpack/search.ts index ecced2db57ee9..477d3a518d164 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/search_xpack/search.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/search_xpack/search.ts @@ -222,28 +222,6 @@ export default function ({ getService }: FtrProviderContext) { verifyErrorResponse(resp.body, 400, 'Request must contain a kbn-xsrf header.'); }); - it('should return 400 when unknown index type is provided', async () => { - const resp = await supertest - .post(`/internal/search/ese`) - .set(ELASTIC_HTTP_VERSION_HEADER, '1') - // TODO: API requests in Serverless require internal request headers - .set(svlCommonApi.getInternalRequestHeader()) - .set('kbn-xsrf', 'foo') - .send({ - indexType: 'baad', - params: { - body: { - query: { - match_all: {}, - }, - }, - }, - }) - .expect(400); - - verifyErrorResponse(resp.body, 400, 'Unknown indexType'); - }); - it('should return 400 if invalid id is provided', async () => { const resp = await supertest .post(`/internal/search/ese/123`) diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/group_by_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/group_by_fired.ts index a80be6721f6af..ccd2aa6edaeaa 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/group_by_fired.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/group_by_fired.ts @@ -255,7 +255,7 @@ export default function ({ getService }: FtrProviderContext) { `${protocol}://${hostname}:${port}/app/observability/alerts?_a=(kuery:%27kibana.alert.uuid:%20%22${alertId}%22%27%2CrangeFrom:%27${rangeFrom}%27%2CrangeTo:now%2Cstatus:all)` ); expect(resp.hits.hits[0]._source?.reason).eql( - `Average system.cpu.total.norm.pct is 80%, above the threshold of 20%. (duration: 1 min, data view: ${DATA_VIEW}, group: host-0)` + `Average system.cpu.total.norm.pct is 80%, above or equal the threshold of 20%. (duration: 1 min, data view: ${DATA_VIEW}, group: host-0)` ); expect(resp.hits.hits[0]._source?.value).eql('80%'); expect(resp.hits.hits[0]._source?.host).eql( diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/p99_bytes_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/p99_bytes_fired.ts new file mode 100644 index 0000000000000..70f3192c117e9 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/p99_bytes_fired.ts @@ -0,0 +1,199 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { cleanup, generate } from '@kbn/infra-forge'; +import { + Aggregators, + Comparator, +} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; +import expect from '@kbn/expect'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const esClient = getService('es'); + const supertest = getService('supertest'); + const esDeleteAllIndices = getService('esDeleteAllIndices'); + const alertingApi = getService('alertingApi'); + const dataViewApi = getService('dataViewApi'); + const logger = getService('log'); + + describe('Custom Threshold rule - P99 - BYTES - FIRED', () => { + const CUSTOM_THRESHOLD_RULE_ALERT_INDEX = '.alerts-observability.threshold.alerts-default'; + // DATE_VIEW should match the index template: + // x-pack/packages/kbn-infra-forge/src/data_sources/composable/template.json + const DATE_VIEW = 'kbn-data-forge-fake_hosts'; + const ALERT_ACTION_INDEX = 'alert-action-threshold'; + const DATA_VIEW_ID = 'data-view-id'; + let infraDataIndex: string; + let actionId: string; + let ruleId: string; + + before(async () => { + infraDataIndex = await generate({ + esClient, + lookback: 'now-15m', + logger, + }); + await dataViewApi.create({ + name: DATE_VIEW, + id: DATA_VIEW_ID, + title: DATE_VIEW, + }); + }); + + after(async () => { + await supertest + .delete(`/api/alerting/rule/${ruleId}`) + .set('kbn-xsrf', 'foo') + .set('x-elastic-internal-origin', 'foo'); + await supertest + .delete(`/api/actions/connector/${actionId}`) + .set('kbn-xsrf', 'foo') + .set('x-elastic-internal-origin', 'foo'); + await esClient.deleteByQuery({ + index: CUSTOM_THRESHOLD_RULE_ALERT_INDEX, + query: { term: { 'kibana.alert.rule.uuid': ruleId } }, + conflicts: 'proceed', + }); + await esClient.deleteByQuery({ + index: '.kibana-event-log-*', + query: { term: { 'rule.id': ruleId } }, + conflicts: 'proceed', + }); + await dataViewApi.delete({ + id: DATA_VIEW_ID, + }); + await esDeleteAllIndices([ALERT_ACTION_INDEX, infraDataIndex]); + await cleanup({ esClient, logger }); + }); + + describe('Rule creation', () => { + it('creates rule successfully', async () => { + actionId = await alertingApi.createIndexConnector({ + name: 'Index Connector: Threshold API test', + indexName: ALERT_ACTION_INDEX, + }); + + const createdRule = await alertingApi.createRule({ + tags: ['observability'], + consumer: 'observability', + name: 'Threshold rule', + ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, + params: { + criteria: [ + { + comparator: Comparator.GT, + threshold: [1], + timeSize: 5, + timeUnit: 'm', + metrics: [ + { name: 'A', field: 'system.network.in.bytes', aggType: Aggregators.P99 }, + ], + }, + ], + alertOnNoData: true, + alertOnGroupDisappear: true, + searchConfiguration: { + query: { + query: '', + language: 'kuery', + }, + index: DATA_VIEW_ID, + }, + }, + actions: [ + { + group: FIRED_ACTIONS_ID, + id: actionId, + params: { + documents: [ + { + ruleType: '{{rule.type}}', + }, + ], + }, + frequency: { + notify_when: 'onActionGroupChange', + throttle: null, + summary: false, + }, + }, + ], + }); + ruleId = createdRule.id; + expect(ruleId).not.to.be(undefined); + }); + + it('should be active', async () => { + const executionStatus = await alertingApi.waitForRuleStatus({ + ruleId, + expectedStatus: 'active', + }); + expect(executionStatus).to.be('active'); + }); + + it('should find the created rule with correct information about the consumer', async () => { + const match = await alertingApi.findRule(ruleId); + expect(match).not.to.be(undefined); + expect(match.consumer).to.be('observability'); + }); + + it('should set correct information in the alert document', async () => { + const resp = await alertingApi.waitForAlertInIndex({ + indexName: CUSTOM_THRESHOLD_RULE_ALERT_INDEX, + ruleId, + }); + + expect(resp.hits.hits[0]._source).property( + 'kibana.alert.rule.category', + 'Custom threshold (Beta)' + ); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.consumer', 'observability'); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.name', 'Threshold rule'); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.producer', 'observability'); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.revision', 0); + expect(resp.hits.hits[0]._source).property( + 'kibana.alert.rule.rule_type_id', + 'observability.rules.custom_threshold' + ); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.uuid', ruleId); + expect(resp.hits.hits[0]._source).property('kibana.space_ids').contain('default'); + expect(resp.hits.hits[0]._source) + .property('kibana.alert.rule.tags') + .contain('observability'); + expect(resp.hits.hits[0]._source).property( + 'kibana.alert.action_group', + 'custom_threshold.fired' + ); + expect(resp.hits.hits[0]._source).property('tags').contain('observability'); + expect(resp.hits.hits[0]._source).property('kibana.alert.instance.id', '*'); + expect(resp.hits.hits[0]._source).property('kibana.alert.workflow_status', 'open'); + expect(resp.hits.hits[0]._source).property('event.kind', 'signal'); + expect(resp.hits.hits[0]._source).property('event.action', 'open'); + + expect(resp.hits.hits[0]._source) + .property('kibana.alert.rule.parameters') + .eql({ + criteria: [ + { + comparator: '>', + threshold: [1], + timeSize: 5, + timeUnit: 'm', + metrics: [{ name: 'A', field: 'system.network.in.bytes', aggType: 'p99' }], + }, + ], + alertOnNoData: true, + alertOnGroupDisappear: true, + searchConfiguration: { index: 'data-view-id', query: { query: '', language: 'kuery' } }, + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/fleet/fleet.ts b/x-pack/test_serverless/api_integration/test_suites/observability/fleet/fleet.ts index 98866bc50f431..73f582f9aa9cb 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/fleet/fleet.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/fleet/fleet.ts @@ -12,7 +12,8 @@ export default function ({ getService }: FtrProviderContext) { const svlCommonApi = getService('svlCommonApi'); const supertest = getService('supertest'); - describe('fleet', function () { + // Failing: See https://github.com/elastic/kibana/issues/176352 + describe.skip('fleet', function () { it('rejects request to create a new fleet server hosts if host url is different from default', async () => { const { body, status } = await supertest .post('/api/fleet/fleet_server_hosts') diff --git a/x-pack/test_serverless/api_integration/test_suites/security/fleet/fleet.ts b/x-pack/test_serverless/api_integration/test_suites/security/fleet/fleet.ts index 98866bc50f431..ba184e7687794 100644 --- a/x-pack/test_serverless/api_integration/test_suites/security/fleet/fleet.ts +++ b/x-pack/test_serverless/api_integration/test_suites/security/fleet/fleet.ts @@ -12,7 +12,8 @@ export default function ({ getService }: FtrProviderContext) { const svlCommonApi = getService('svlCommonApi'); const supertest = getService('supertest'); - describe('fleet', function () { + // FLAKY: https://github.com/elastic/kibana/issues/176399 + describe.skip('fleet', function () { it('rejects request to create a new fleet server hosts if host url is different from default', async () => { const { body, status } = await supertest .post('/api/fleet/fleet_server_hosts') diff --git a/yarn.lock b/yarn.lock index 099e1b64cf509..d75766cfa5b0e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4348,6 +4348,10 @@ version "0.0.0" uid "" +"@kbn/data-view-utils@link:packages/kbn-data-view-utils": + version "0.0.0" + uid "" + "@kbn/data-views-plugin@link:src/plugins/data_views": version "0.0.0" uid "" @@ -26016,13 +26020,6 @@ react-resizable@^3.0.4: prop-types "15.x" react-draggable "^4.0.3" -react-resize-detector@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/react-resize-detector/-/react-resize-detector-7.1.1.tgz#18d5b84909d5ab13abe0a68ddf0fb8e80c553dfc" - integrity sha512-rU54VTstNzFLZAmMNHqt8xINjDWP7SQR05A2HUW0OGvl4vcrXzgaxrrqAY5tZMfkLkoYm5u0i0qGqCjdc2jyAA== - dependencies: - lodash "^4.17.21" - react-reverse-portal@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/react-reverse-portal/-/react-reverse-portal-2.1.0.tgz#3c572e1c0d9e49b8febf4bf2fd43b9819ce6f508"